成员函数指针的解决方法是一个糟糕的黑客?

时间:2013-12-10 15:54:59

标签: c++ pointers member-function-pointers reinterpret-cast

所以我有一个变体类,我最近添加了存储指针到成员函数数据的能力。它使用以下代码来实现它。

class Variant
{
    void* _value;

    template <typename T1>
    void Variant::ThisCall( T1* pThis )
    {
        typedef void(T1::* fptr)( );
        fptr a;
        int* iiVal = new (&a) int;
        *iiVal = *((int*)_value);
        (pThis->*a)( );
    }
};

// usage
Variant myfunc = &SomeClass::SomeMethod;
SomeClass* s = new SomeClass( );
myfunc.ThisCall( s );

我为此解决方案解决的最大问题是指向成员函数的指针无法转换为void *。因此赋值运算符排序基本上与此操作相反。它接受给定的数据,将其作为int指针(如果它是指针本身)将其屏蔽,并将int指针指定给void *,这是完全合法的。

所以我的问题是这样的:为什么我觉得这是解决这个问题的可怕方法?我觉得这是一个很大的黑客,它必须有一些严重的问题,但是我已经在这个问题上如此深入了几天,以至于我看不到它。谢谢!

[编辑#1]

一位评论者指出,这可能不适用于虚拟方法。我已经使用以下代码进行了测试,似乎可以查看。

class ImplA : public Base
{
public:
    virtual void Print( )
    {
        cout << "ImplA print\n";
    }
};

class ImplB : public Base
{
public:
    virtual void Print( )
    {
        cout << "ImplB print\n";
    }
};

class ImplC : public ImplA
{
public:
    virtual void Print( )
    {
        cout << "ImplC print\n";
    }
};

// usage
Variant x = &Base::Print;
auto b = new ImplA; // replace ImplA with ImplB or ImplC and it works as expected
x.ThisCall( b );

有关其他信息,我正在使用VS2010作为我的编译器。谢谢!

[编辑#2]

为了提供上下文,我现在已经在这个变种类上工作了一段时间,我试图让它支持你可以抛出的任何东西。在这样做时,我考虑了函数指针和成员函数指针。然后我想出了这个,并想知道这个解决方案实际上是多么可靠。演员和句法对我来说是第一个红旗,但我想,由于它所拥有的数据的差异,这只是领土。但我仍然不相信这应该如何运作。

2 个答案:

答案 0 :(得分:1)

忽略别名违规,这本身就会使你的代码变得非法,你所做的就等同于:

    typedef void(T1::* fptr)( );
    fptr a;
    memcpy(&a, _value, sizeof(int));
    (pThis->*a)( );

为什么这是不便携的应该是显而易见的;无法保证fptrint的大小相同,因此您可能会部分初始化其存储空间或将其溢出。

如果您将sizeof(int)替换为sizeof(fptr),这将是合法的,并确保_value指向的存储空间大到足以包含fptr。但是,您仍应使用memcpy而不是别名; memcpy保证可以正常工作(3.9p2),而别名可能会导致难以检测的错误,这些错误通常会在优化过程中显示或改变行为。

答案 1 :(得分:0)

如评论中所述,此代码不是非常便携且安全。如果你只是存储指向函数的指针,我建议使用std :: function或boost :: function wrappers:

template <typename T>
class Variant {
    std::function<void(T*)> fun;
public:
    Variant(void (T:: *ptr)() ) : fun(ptr) {
    }

    void ThisCall(T* pThis) {
        fun(pThis);
    }
};

Variant<SomeClass> myfunc = &SomeClass::SomeMethod;
SomeClass* s = new SomeClass( );
myfunc.ThisCall( s );

但是如果你真的想存储任何东西,为什么不使用boost :: any库?

class VariantWithAny {
    boost::any val;
public:
    VariantWithAny() {}

    VariantWithAny(const boost::any& val) : val(val) {}

    VariantWithAny& operator=(const boost::any& val) {
        this->val = val;
        return *this;
    }

    template <typename T>
    void ThisCall(T* pThis) {
        typedef void (T::* fptr)();
        fptr a = boost::any_cast<fptr>(val);
        (pThis->*a)( );
    }
};

VariantWithAny myfunc2(&SomeClass::SomeMethod1);
myfunc2 = &SomeClass::SomeMethod2;
SomeClass* s2 = new SomeClass( );
myfunc2.ThisCall( s2 );

boost :: any_cast是安全的,如果类型不匹配,将抛出异常(boost :: bad_any_cast)。

[编辑] :boost :: any使用的技巧是将值存储在继承自纯虚拟占位符的模板持有者类中。这就是它几乎可以保存任何值而不必将其转换为(void *)的方式。查看实现 - 这是一个非常小的文件。