3

I want to convert some const void* object into a function pointer:

std::unordered_map<std::string, const void*> originals_;

template <typename R, typename... Args>
R CallOriginal(const std::string& name, Args... args) {
  return reinterpret_cast<R (*const)(Args...)>(originals_[name])(args...);
}

But, to my surprise, I get the following error message:

error: reinterpret_cast from 'mapped_type' (aka 'const void *') to 'int (*const)(int)' casts away qualifiers

First of all, does it even make sense to use the const function pointer? If it does, then how can I legally make the casting?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
abyss.7
  • 13,882
  • 11
  • 56
  • 100
  • 1
    Why are you coercing function pointers to `const void*` to store them in the map in the first place? Why don't you just store function pointers? – Brian Bi Feb 15 '15 at 08:20
  • 1
    Because I want to store pointers to the functions with different signatures. More over, the library that I use to manipulate the function pointers does convert them to `void*` and accept them as `void const*`. – abyss.7 Feb 15 '15 at 08:24
  • Interestingly, g++4.9.2 seems to accept this conversion. IIRC, there's also a proposal to remove the restriction that `reinterpret_cast` may not cast away cv-qualifiers. – dyp Feb 15 '15 at 08:46
  • 5
    That just sounds like a terrible idea. Since function types are known at compile time, there can only be a finite number of them, so you should have a separate map for each type. Don't fight the type system; make it work for you. I never understand why people try to use C++ as though it were a dynamically typed language. – Brian Bi Feb 15 '15 at 08:48
  • 1
    @Brian The question is not about idea - it's more about syntax and curiosity. And I don't want the map for each separate signature, I want the map for all function replacements by their names - not to store a sole function pointer in a separate variable, otherwise, that really would be terrible. Also I use the third-party C library, AFAIK casting everything to `void*` in C is not that bad as in C++. – abyss.7 Feb 15 '15 at 08:54
  • @dyp I'm using the trunk clang. Looks like it's a compiler specific... – abyss.7 Feb 15 '15 at 08:56
  • 1
    intel 15.0.2 allows this as well. Of course everything about this conversion is implementation-defined (per `5.2.10[expr.reinterpret.cast]/8`) – Cubbi Feb 15 '15 at 15:54
  • You are trying to invent your own reflection ? – Victor Gubin Mar 11 '20 at 13:16
  • If you throw away the function's type, how do you know *exactly* what to call it with? See also: https://stackoverflow.com/questions/45715219/store-functions-with-different-signatures-in-a-map/45718187#45718187 – Caleth Mar 11 '20 at 13:45

2 Answers2

2

In order to call any function by it's name you can use std::any combined with standard library functor std::function

Be aware, caller must know the signature, e.g. parameter types and return type can not be deduced.

An example:

#include <any>
#include <functional>
#include <string>
#include <unordered_map>

#include <iostream>

static int foo(int a, const std::string& b) {
    std::cout << "foo(" << a << ',' << b << ");" << std::endl;
    return 0;
}

static void bar(float a, const std::string& b) {
    std::cout << "bar(" << a << ',' << b << ");" << std::endl;
}

class call_function_by_name {
public:
    explicit call_function_by_name(std::unordered_map<std::string, std::any>&& table):
        table_(table)
    {}
    template <typename R,typename... ArgTypes>
    R call(const std::string& name,ArgTypes... args) const {
       typedef std::function<R(ArgTypes...)> functor_t;
       std::any anyf = table_.at(name);
       // call function by name
       functor_t functor = std::any_cast<functor_t>( anyf ) ;
       return functor( args... );
    }
private:
    std::unordered_map<std::string, std::any> table_;
};


int main(int argc, const char** argv)
{

    std::unordered_map<std::string, std::any> exportTable;
    exportTable.emplace("foo", std::function<int(int,const std::string&)>(foo) );
    exportTable.emplace("bar", std::function<void(float,const std::string&)>(bar) );

    call_function_by_name bus( std::move(exportTable) );
    int ret = bus.call<int,int,const std::string&>("foo", 1, std::string("bus foo") );
    std::cout << "Foo returned " << ret << std::endl;
    bus.call<void,float,const std::string&>("bar", 2.0F, "bus bar");

    return 0;
}
Victor Gubin
  • 2,782
  • 10
  • 24
1

You want to erase function signature and store just function pointer.

For this you can cast function pointers to other function pointers, like to typedef void(*p)().

C++ can work on an architecture where data memory and code memory do not interact and have different pointer size. So cast function pointer to data pointer is generally not safe

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79