Check if function pointer is registered

Context

(C ++ 11) As part of the protection in the serialization code snippet, I want to check if the function pointer is known. (Otherwise, the deserialization mechanism will probably fail).

This is the (simplified) part of the code that illustrates the mechanism:

template <typename Fun>
struct FunctionCallRegistry
{
    static std::unordered_map<Fun, std::string>& FunctionMap()
    {
        static std::unordered_map<Fun, std::string> map;
        return map;
    }

    static void Register(Fun function, std::string name)
    {
        auto& map = FunctionMap();
        auto it = map.find(function);
        if (it == map.end()) 
        {
            map.insert(std::pair<Fun, std::string>(function, name));
            // other code follows...
        }
    }

    static void Ensure(Fun function) 
    {
        auto& map = FunctionMap();
        auto it = map.find(function);
        if (it == map.end())
        {
            throw std::exception("Function not found.");
        }
    }
};

// Registration code:
struct FunctionCallRegistryInitializer
{
    template <typename Ret, typename... Args>
    explicit FunctionCallRegistryInitializer(Ret(*function)(Args...),
                                             const char* name)
    {
        FunctionCallRegistry<Ret(*)(Args...)>::Register(function, name);
    }
};

#define RegisterFunction(fcn) FunctionCallRegistryInitializer fcn_reg_##__COUNTER__(&fcn, #fcn);

Serialization is now easy, given that we can find a function and emit a name. Similarly, deserialization looks through the name and emits a factory function wrapped in a class (this is the easy part).

Registration works as follows: basically for each function I call Registerwith the function pointer and name (which is used to serialize (de)). There, a macro is involved here and several auxiliary classes to make plumbing. For instance:.

struct Testje
{
    static int test(int lhs, int rhs) {
        std::cout << lhs << " + " << rhs << std::endl;
        return lhs + rhs;
    }
};

RegisterFunction(Testje::test);

Fun - f.ex. void (*fcn)(int, int). , , , -. "", ...

MSV++, , , :

error C2338: The C++ Standard doesn't provide a hash for this type. 

fcn , , , .

, - ; - , Fun . , name , , .

: ? , ?

+4
4

, , . , , , , . Mikael!

, . , , , (, , ).

, -.

: - (, 1). :

template <typename Fun>
struct FunctionCallFactory
{
private:
    static std::vector<std::pair<Fun, std::string>>& FunctionMap()
    {
        static std::vector<std::pair<Fun, std::string>> map;
        return map;
    }

public:
    static void Register(Fun function, std::string name)
    {
        auto& map = FunctionMap();
        for (auto& it : FunctionMap())
        {
            if (it.first == function)
            {
                throw std::exception("Duplicate registration of function. Not allowed.");
            }
        }
        map.push_back(std::pair<Fun, std::string>(function, name));
    }

    // etc.
};

, Fun , . , .

Fun , typeid -. .

+1

, -. :

// Registration code:
struct FunctionCallRegistryInitializer
{

    // Here I changed Ret(*function) to Ret(T::*function)
    template <typename T, typename Ret, typename... Args>
    explicit FunctionCallRegistryInitializer(Ret(T::*function)(Args...),
                                             const char* name)
    {
        FunctionCallRegistry<Ret(T::*)(Args...)>::Register(function, name);
    }
};

// We need it for use use member-function test like key value in map
namespace std {
    template <> 
    struct hash<decltype(&Testje::test)>
    {
        size_t operator()(decltype(&Testje::test) x) const
        {
            return typeid(x).hash_code();
        }
    };
}

RegisterFunction(Testje::test);

, -:

FunctionCallRegistry<decltype(&Testje::test)>::Ensure(&Testje::test);

, , , .

+1

- . 2 .

-

, - , . , :

struct Testje
{
    int test(int lhs, int rhs) {
        std::cout << lhs << " + " << rhs << std::endl;
        return lhs + rhs;
    }
};

template<class T, int(T::*mf)(int, int)>
int wrapper(T& obj, int lhs, int rhs) {
        return (obj.*mf)(lhs, rhs);
}
...
FunctionCallRegistry<int (*)(Testje&, int, int)> reg;
reg.Register(&(wrapper<Testje, &Testje::test>), "test");

++ 11 ( , , Quentin dyp - . )

- -

- unordered_map, . , :

class Testje{
public:
    int test(int lhs, int rhs) {
        std::cout << lhs << " + " << rhs << std::endl;
        return lhs + rhs;
    }
    static int myhash(int (Testje::*f)(int, int) f) {
        // for each and every member function with that signature return a different int
        if (f == &Testje::test) {
             return 0;
        }
        return -1;
    }
    class Hash {
    public:
        size_t operator( )(int (Testje::*f)(int, int) f) const {
            return myhash(f);
        }
    };
};

FunctionCallRegistry, -:

template <typename Fun, class Hash = std::hash<Fun>>
struct FunctionCallRegistry
{
    // not static to allow multiple registries
    std::unordered_map<Fun, std::string, Hash> _map; 

    std::unordered_map<Fun, std::string, Hash>& FunctionMap() // returns a reference ...
    {
        return _map;
    }

    void Register(Fun function, std::string name)
    {
        std::unordered_map<Fun, std::string, Hash>& map = FunctionMap();
        auto it = map.find(function);
        if (it == map.end())
        {
            map.insert(std::pair<Fun, std::string>(function, name));
            // other code follows...
        }
    }

    void Ensure(Fun function)
    {
        std::unordered_map<Fun, std::string, Hash>& map = FunctionMap();
        auto it = map.find(function);
        if (it == map.end())
        {
            throw std::exception();
        }
    }
};

:

FunctionCallRegistry<int (Testje::*)(int, int), Testje::Hash> reg;

reg.Register(&Testje::test, "test");
reg.Ensure(&Testje::test);

, -.

: -.

+1

, . -?

- (void * &).

" Z"... , , . "", , , . .

0

Source: https://habr.com/ru/post/1584024/


All Articles