You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为C++仿函数创建兼容C风格函数指针并修复链接错误?

问题:将带状态的C++仿函数转为C风格函数指针

需求背景

本文是此前探索的延续,目标是创建bool(double, double)类型的C风格函数指针,指向签名兼容的带状态C仿函数(此前方案在constexpr使用上存在限制)。需要实现如下Python代码的等效C逻辑:

#! /usr/bin/env python3

class FixPrecCompare:
    '''Compares two floats to the given precision'''
    def __init__(self, numDigits: int) -> None:
        self.factor = 10 ** numDigits
    def __call__(self, a: float, b: float) -> bool:
        return int(a * self.factor) == int(b * self.factor)
    
class AreClose:
    '''Same as Python's inbuilt math.isclose [which should be called areclose :-)]'''
    def __init__(self, absTol = 0, relTol = 1e-09) -> None:
        self.absTol = absTol; self.relTol = relTol
    def __call__(self, a: float, b: float) -> bool:
        diff = abs(a - b)
        return diff <= self.absTol or \
            diff <= abs(self.relTol * a) or \
            diff <= abs(self.relTol * b)

# Now for signature compatibility (which is not a matter in Python)
# I make a function which internally uses the functor
def makeApproxCompare(FunctorType: type, *args):
    functor = FunctorType(*args)
    def fn(a: float, b: float) -> bool:
        return functor(a, b)
    return fn

approxCompare2 = makeApproxCompare(FixPrecCompare, 2)
approxCompare3 = makeApproxCompare(FixPrecCompare, 3)
areClose = makeApproxCompare(AreClose, 1e-3)

a, b = 4.561, 4.569
print(f"Compare {a:.3f}, {b:.3f} to 2 decimals: {approxCompare2(a, b)}")
print(f"Compare {a:.3f}, {b:.3f} to 3 decimals: {approxCompare3(a, b)}")

a, b = 2.7099, 2.7101
print(f"Compare {a} to {b} to 3 decimals: {approxCompare3(a, b)}")
print(f"Compare {a} to {b} using areClose: {areClose(a, b)}")

尝试的C++代码

typedef bool(*SuccessTester)(double, double);

#include <cmath>
#include <iostream>
using namespace std;

struct FixPrecCompare
{
    FixPrecCompare(int numDigits): factor(pow(10, numDigits)) {}
    bool operator()(double a, double b) { return int(a * factor) == int(b * factor); }
private:
    double factor;
};

struct AreClose
{
    AreClose(double absTol, double relTol): abs_tol{absTol}, rel_tol{relTol} {}
    bool operator()(double a, double b)
    {
        double diff = fabs(a - b);
        return diff <= abs_tol ||
            diff <= fabs(rel_tol * a) ||
            diff <= fabs(rel_tol * b);
    }
private:
    double abs_tol, rel_tol;
};

template<typename Functor>
struct Helper
{
    static Functor fr;
    static bool func(double a, double b) { return fr(a, b); }
};

template<typename Functor, typename ... ArgTypes>
SuccessTester makeSuccessTester(ArgTypes ... args)
{
    Helper<Functor> h;
    h.fr = Functor{args ...};
    return &h.func;
}

int main()
{
    SuccessTester fp;
    cout << boolalpha;

    double a, b;
    
    a = 4.561; b = 4.569;
    fp = makeSuccessTester<FixPrecCompare>(2);
    cout << "Compare " << a << ", " << b << " to 2 decimals: " << fp(a, b) << endl;
    fp = makeSuccessTester<FixPrecCompare>(3);
    cout << "Compare " << a << ", " << b << " to 3 decimals: " << fp(a, b) << endl;
    
    a = 2.7099; b = 2.7101;
    cout << "Compare " << a << ", " << b << " to 3 decimals: " << fp(a, b) << endl;
    fp = makeSuccessTester<AreClose>(1e-3, 1e-10);
    cout << "Compare " << a << ", " << b << " using areClose: " << fp(a, b) << endl;
}

编译链接错误

GCC下的错误信息

/usr/bin/ld: /tmp/ccEkJxbk.o: warning: relocation against `_ZN6HelperI14FixPrecCompareE2frE' in read-only section `.text._ZN6HelperI14FixPrecCompareE4funcEdd[_ZN6HelperI14FixPrecCompareE4funcEdd]'
/usr/bin/ld: /tmp/ccEkJxbk.o: in function `bool (*makeSuccessTester<FixPrecCompare, int>(int))(double, double)':
<src>:(.text._Z17makeSuccessTesterI14FixPrecCompareJiEEPFbddEDpT0_[_Z17makeSuccessTesterI14FixPrecCompareJiEEPFbddEDpT0_]+0x38): undefined reference to `Helper<FixPrecCompare>::fr'
/usr/bin/ld: /tmp/ccEkJxbk.o: in function `bool (*makeSuccessTester<AreClose, double, double>(double, double))(double, double)':
<src>:(.text._Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_[_Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_]+0x4e): undefined reference to `Helper<AreClose>::fr'
/usr/bin/ld: <src>:(.text._Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_[_Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_]+0x55): undefined reference to `Helper<AreClose>::fr'
/usr/bin/ld: /tmp/ccEkJxbk.o: in function `Helper<FixPrecCompare>::func(double, double)':
<src>:(.text._ZN6HelperI14FixPrecCompareE4funcEdd[_ZN6HelperI14FixPrecCompareE4funcEdd]+0x2b): undefined reference to `Helper<FixPrecCompare>::fr'
/usr/bin/ld: /tmp/ccEkJxbk.o: in function `Helper<AreClose>::func(double, double)':
<src>:(.text._ZN6HelperI8AreCloseE4funcEdd[_ZN6HelperI8AreCloseE4funcEdd]+0x2b): undefined reference to `Helper<AreClose>::fr'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

Clang下的错误信息

<src>:40:7: warning: instantiation of variable 'Helper<FixPrecCompare>::fr' required here, but no definition is available [-Wundefined-var-template]
    h.fr = Functor{args ...};
      ^
<src>:52:10: note: in instantiation of function template specialization 'makeSuccessTester<FixPrecCompare, int>' requested here
    fp = makeSuccessTester<FixPrecCompare>(2);
        ^
<src>:32:20: note: forward declaration of template entity is here
    static Functor fr;
                  ^
<src>:40:7: note: add an explicit instantiation declaration to suppress this warning if 'Helper<FixPrecCompare>::fr' is explicitly instantiated in another translation unit
    h.fr = Functor{args ...};
      ^
<src>:40:7: warning: instantiation of variable 'Helper<AreClose>::fr' required here, but no definition is available [-Wundefined-var-template]
    h.fr = Functor{args ...};
      ^
<src>:59:10: note: in instantiation of function template specialization 'makeSuccessTester<AreClose, double, double>' requested here
    fp = makeSuccessTester<AreClose>(1e-3, 1e-10);
        ^
<src>:32:20: note: forward declaration of template entity is here
    static Functor fr;
                  ^
<src>:40:7: note: add an explicit instantiation declaration to suppress this warning if 'Helper<AreClose>::fr' is explicitly instantiated in another translation unit
    h.fr = Functor{args ...};
      ^
2 warnings generated.
/usr/bin/ld: /tmp/<src>-ce7af1.o: in function `bool (*makeSuccessTester<FixPrecCompare, int>(int))(double, double)':
<src>:(.text._Z17makeSuccessTesterI14FixPrecCompareJiEEPFbddEDpT0_[_Z17makeSuccessTesterI14FixPrecCompareJiEEPFbddEDpT0_]+0x1a): undefined reference to `Helper<FixPrecCompare>::fr'
/usr/bin/ld: /tmp/<src>-ce7af1.o: in function `bool (*makeSuccessTester<AreClose, double, double>(double, double))(double, double)':
<src>:(.text._Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_[_Z17makeSuccessTesterI8AreCloseJddEEPFbddEDpT0_]+0x28): undefined reference to `Helper<AreClose>::fr'
/usr/bin/ld: /tmp/<src>-ce7af1.o: in function `Helper<FixPrecCompare>::func(double, double)':
<src>:(.text._ZN6HelperI14FixPrecCompareE4funcEdd[_ZN6HelperI14FixPrecCompareE4funcEdd]+0x1f): undefined reference to `Helper<FixPrecCompare>::fr'
/usr/bin/ld: /tmp/<src>-ce7af1.o: in function `Helper<AreClose>::func(double, double)':
<src>:(.text._ZN6HelperI8AreCloseE4funcEdd[_ZN6HelperI8AreCloseE4funcEdd]+0x1f): undefined reference to `Helper<AreClose>::fr'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

求助

我不确定如何修复代码以实现预期功能,恳请帮助!


内容的提问来源于stack exchange,提问作者jamadagni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:22:01