0

Iam new in C++ and do not know what am I doing so please help. The issue is, when Iam trying to do this in Visual Studio 2022, it does not compile:

I replaced std::function with this in my codebase. It supposed to be used as a delegate in event system.

TFunction.h:

namespace t3d
{
template<typename T> class TFunction;

template<typename Return_T, typename... Args_T>
class TFunction<Return_T(Args_T...)>
{
public:
    
// Aliases:

    template<class C>
    using CallbackMember_T = Return_T(C::*)(Args_T...);
    
    using CallbackStatic_T = Return_T(*)(Args_T...);

// Constructors:

    TFunction()
        : CallbackMember   (nullptr)
        , FunctionPointers { &TFunction::CallMember, &TFunction::CallStatic } 
        , CallbackStatic   (nullptr)
        , Instance         (nullptr)
        , Index            (0)
    {
    }

    template<class C>
    TFunction(C* Instance, CallbackMember_T<C> Callback)
        : CallbackMember   (reinterpret_cast<CallbackMember_T<FInstance>>(Callback))
        , FunctionPointers { &TFunction::CallMember, &TFunction::CallStatic }
        , CallbackStatic   (nullptr)
        , Instance         (reinterpret_cast<FInstance*>(Instance))
        , Index            (0)
    {
    }

    TFunction(CallbackStatic_T Callback)
        : CallbackMember   (nullptr)
        , FunctionPointers { &TFunction::CallMember ,  &TFunction::CallStatic }
        , CallbackStatic   (Callback)
        , Instance         (nullptr)
        , Index            (1)
    {
    }

// Functions:

    template<class C>
    constexpr void Bind(C* Instance, CallbackMember_T<C> Callback) noexcept
    {
        this->Instance       = reinterpret_cast<FInstance*>(Instance);
        this->CallbackMember = reinterpret_cast<CallbackMember_T<FInstance>>(Callback);
        this->CallbackStatic = nullptr;
        this->Index          = 0;
    }

    constexpr void Bind(CallbackStatic_T Callback) noexcept
    {
        this->Instance       = nullptr;
        this->CallbackMember = nullptr;
        this->CallbackStatic = Callback;
        this->Index          = 1;
    }

    constexpr Return_T Invoke(Args_T... Args) const
    {
        return (this->*FunctionPointers[Index])(std::forward<Args_T>(Args)...);
    }

    template<class C>
    constexpr bool8 IsEqual(CallbackMember_T<C> Callback) const
    {
        return reinterpret_cast<CallbackMember_T<C>>(CallbackMember) == Callback;
    }

    constexpr bool8 IsEqual(CallbackStatic_T Callback) const noexcept
    {
        return CallbackStatic == Callback;
    }

    constexpr bool8 IsEmpty() const noexcept
    {
        return (Instance == nullptr) && (CallbackMember == nullptr) && (CallbackStatic == nullptr);
    }

// Operators:

    explicit operator bool () const noexcept
    {
        return (Instance && CallbackMember) || CallbackStatic;
    }

    constexpr bool8 operator == (const TFunction& Right) const noexcept
    {
        return (Instance == Right.Instance) && (CallbackMember == Right.CallbackMember) && (CallbackStatic == Right.CallbackStatic);
    }

    constexpr bool8 operator != (const TFunction& Right) const noexcept
    {
        return (Instance != Right.Instance) || (CallbackMember != Right.CallbackMember) || (CallbackStatic != Right.CallbackStatic);
    }

private:

    using CallbackInternal_T = Return_T(TFunction::*)(Args_T...) const;

// Private Functions:

    Return_T CallMember(Args_T... Args) const
    {
        return (Instance->*CallbackMember)(std::forward<Args_T>(Args)...);
    }

    Return_T CallStatic(Args_T... Args) const
    {
        return (*CallbackStatic)(std::forward<Args_T>(Args)...);
    }

// Variables:

    class FInstance {};

    CallbackMember_T<FInstance> CallbackMember;
    CallbackInternal_T          FunctionPointers[2];        
    CallbackStatic_T            CallbackStatic;
    FInstance*                  Instance;
    uint64                      Index;
};
}

Main.cpp:

Here am I testing TFunction with this exact code:

struct FData
{
    int A;
    int B;
};

struct FResult
{
    bool Success;
};

class FBase1
{
public:

    virtual FResult Function1(FData Data)
    {
        return FResult{ true };
    }
};

class FBase2
{
public:

    virtual FResult Function2(FData Data)
    {
        return FResult{ true };
    }
};

class FDerived : public FBase1, public FBase2
{
public:

    FResult Function1(FData Data) override
    {
        return FResult{ true };
    }

    FResult Function2(FData Data) override
    {
        return FResult{ true };
    }

    FResult Function3(FData Data)
    {
        return FResult{ true };
    }
};

FResult FunctionGlobal(FData Data)
{
    return FResult{ true };
}

t3d::int32 main()
{
    FDerived Derived;

    t3d::TFunction<FResult(FData)> TestFunction;    

    TestFunction.Bind(&Derived, &FDerived::Function1); // Error C2440
    TestFunction.Bind(&Derived, &FDerived::Function2); // Error C2440
    TestFunction.Bind(&Derived, &FDerived::Function3); // Error C2440

    return 0;
}

Error says: 'reinterpret_cast': cannot convert from 'Return_T (__cdecl FDerived::* )(FData)' to 'Return_T (__cdecl t3d::TFunction<FResult (FData)>::FInstance::* )(FData)'

Any ideas?

Edited: Binding works fine with nested inheritance or with no inheritance, but not with inheritance from multiple classes.

Lex
  • 29
  • 1
  • 4
  • 1
    You need to add typename in `template using Callback_T = void(C::*)();`, and you need to make the methods public. – user3188445 Jul 04 '22 at 00:30
  • 2
    yeah ... i don't belive that is the error you are getting from exactly that code. Post a minimum reproducible example if you want to get a real answer; otherwise, what @ user3188445 says: `template blah blah` is not the correct syntax for an alias template and class members are private by default so you can't access `Derived::Function1` etc externally unless you declare those to be public – jwezorek Jul 04 '22 at 00:33
  • You guys seem to be interested in real example. So be it, i will edit the post to show full code. – Lex Jul 04 '22 at 00:36
  • @jwezorek Sure, been trying to simplify things, but made it worse. – Lex Jul 04 '22 at 00:53

1 Answers1

0

Thanks guys for the participation, i found the answer in this thread: Pointers to members representations

The issue was - member function pointers can have different representations because of the differences in the underlying structs and different sizes. This is why they are not always can be reinterpreted as a different member function pointers. It is actually a Microsoft specific feature and can be turned off by using /vmg, or as follows:

#if _WIN64
#pragma pointers_to_members( full_generality, multiple_inheritance )
#endif

where multiple_inheritance corresponds to the biggest representation.

And it turned out that "multiple_inheritance" is actually a C++ standard representation.

Lex
  • 29
  • 1
  • 4