如何为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
相关产品推荐
相关产品推荐

