C 风格的 C++ 函子包装器

问题描述 投票:0回答:1

这是我正在尝试的事情的进一步发展我最近的问题

我想创建一个

bool(double, double)
C 风格的函数指针,指向具有兼容签名的仿函数。 (进一步尝试的原因是早期的方法似乎对可以进行
constexpr
编辑的内容有限制。)

如何在 C++ 中执行与以下工作 Python 代码等效的操作:

#! /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)}")

我的尝试:

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)

但我不确定如何修复代码以使其按预期工作。请帮忙!谢谢!

c++ templates function-pointers
1个回答
0
投票

您的方法的主要问题是您试图同时为同一个静态成员分配多个不同的值。

Helper<FixPrecCompare>::fr
是一个唯一的变量,因为您将其声明为静态,并且它不能同时保存
FixPrecCompare(2)
FixPrecCompare(3)
。因此,即使您进行编译,它也将作为一个全局变量,包含您分配给它的最后一个值,无论如何,这都不是一件好事。我在这里看到 2 个解决方案:要么将所有内容都设为模板参数,要么使用
std::function
提供的类型擦除。

  1. 从 C++20 开始工作,因为我们需要
    double
    模板参数:
#include <cmath>
#include <iostream>
using namespace std;

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

template<int numDigits>
struct FixPrecCompare
{
    static bool compare(double a, double b) { return int(a * factor) == int(b * factor); }
private:
    static constexpr double factor = pow(10, numDigits);
};

template<double abs_tol, double rel_tol>
struct AreClose
{
    static bool compare(double a, double b)
    {
        double diff = fabs(a - b);
        return diff <= abs_tol ||
            diff <= fabs(rel_tol * a) ||
            diff <= fabs(rel_tol * b);
    }
};

template<typename Functor>
SuccessTester makeSuccessTester()
{
    return &Functor::compare;
}

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;
}
#include <cmath>
#include <iostream>
#include <functional>
using namespace std;

using SuccessTester = std::function<bool(double, double)>;

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, typename ... ArgTypes>
SuccessTester makeSuccessTester(ArgTypes ... args)
{
    return Functor{args ...};
}

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.