In C++ 11, How to Invoke an Arbitrary Callable Object

In c++ 11, how to invoke an arbitrary callable object?

This is exactly what std::invoke does, but it won't be standard until C++17. You can make your own version, but it can be pretty complicated if it is fully general.

Here's the basic idea for two cases (code taken from cppreference.com):

template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}

template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}

why std::function that store a call to member function can have two different callable type

std::function stores and can "invoke any CopyConstructible Callable target".

The Callable named requirement is defined as follows:

 if f is a pointer to member function of class T: 

If std::is_base_of<T, std::remove_reference_t<decltype(t1)>>::value
is true, then INVOKE(f, t1, t2, ..., tN) is equivalent to
(t1.*f)(t2, ..., tN)

Or, in other words: if after removing any reference qualifier on the first parameter the end result is the member function's class (or a subclass), the member function gets invoked for the instance of the class.

otherwise, if t1 does not satisfy the previous items,
then INVOKE(f, t1, t2, ..., tN) is equivalent to
((*t1).*f)(t2, ..., tN).

Otherwise: cross your fingers, hope that the first parameter is a pointer to an instance of the member function's class, and invoke it via the given pointer (hoping against hope that it's a pointer, or something that pretends that it's a pointer).

Final results: either one is equivalent to the other.

Pass callable object by value, assign it to pointer member

Yes this is undefined behavior. After the closing brace of the constructor callable is destroyed and you have a dangling pointer.

The reason you are not seeing adverse effects is that you really aren't using the instance after it goes out of scope. The function call operator is stateless so it is not trying to access the memory that it no longer owns.

If we add some state to the callable like

class Callable
{
int foo;
public:
Callable (int foo = 20) : foo(foo) {}
void operator()(int x)
{
printf("x = %d\n", x*foo);
}
};

and then we use

int main()
{
UsesTheCallable<Callable>* u = NULL;
{
Callable c(50);
u = new UsesTheCallable<Callable>(c);
}
u->call();
delete u;
return 0;
}

Then you could see this bad behavior. In that run it outputs x = 772773112 which is not correct.

Calling arbitrary function in C++

Basic idea

The basic idea is that you want to encapsulate your functions in some wrapper object which would handle some generic input/output and map them to what your underlying function expects.

First of all let's create a type for storing any value:

// Dummy implementation which only works for some type.
class Value {
long value_;
public:
template<class T>
T get()
{
return (T) value_;
}
template<class T>
Value& operator=(T const& x)
{
value_ = x;
return *this;
}
};

Let's hide our function using generic arguments:

typedef std::function<Value(std::vector<Value>&)> Function;

We now want to wrap any function pointer, in order to conform to this signature. The wrapper function should unwrap the arguments, call the real function and wrap the result in a Value:

template<class F> class FunctionImpl;

template<class R, class... T>
class FunctionImpl<R(*)(T...)>
{
R(*ptr)(T... args);
template<std::size_t... I>
Value call(std::vector<Value>& args, integer_sequence<std::size_t, I...>)
{
Value value;
value = ptr(args[I].get< typename std::tuple_element<I, std::tuple<T...>>::type >()...);
return value;
}
public:
FunctionImpl(R(*ptr)(T... args)) : ptr(ptr) {}
Value operator()(std::vector<Value>& args)
{
constexpr std::size_t count = std::tuple_size<std::tuple<T...>>::value;
if (args.size() != count)
throw std::runtime_error("Bad number of arguments");
return call(args, make_integer_sequence<std::size_t, std::tuple_size<std::tuple<T...>>::value>());
}
};

integer_sequence and make_integer_sequence are part of the standard C++17 library but you can write your own implementation.

We now define a type for registering the callable functions:

class Functions {
private:
std::unordered_map<std::string, Function> functions_;
public:
template<class F>
void add(std::string const& name, F f)
{
functions_[name] = FunctionImpl<F>(std::move(f));
}
Value call(std::string name, std::vector<Value>& args)
{
return functions_[name](args);
}
};

And we can use it:

int foo(int x, int y)
{
std::printf("%i %i\n", x, y);
return x + y;
}

int main()
{
Functions functions;
functions.add("foo", &foo);

std::pair<std::string, std::vector<Value>> request = parse_request();
Value value = functions.call(request.first, request.second);
generate_answer(value);

return 0;
}

with the dummy RPC communication functions:

std::pair<std::string, std::vector<Value>> parse_request()
{
std::vector<Value> args(2);
args[1] = 8;
args[0] = 9;
return std::make_pair("foo", std::move(args));
}

void generate_answer(Value& value)
{
std::printf("%i\n", value.get<int>());
}

We get:

8 9
17

Of course, this is highly simplified and you'll face many issues if you want to generalize it:

  • you might want to propagate exceptions as well;

  • integer types (eg. long) do not have the same size on different platforms;

  • it starts gettings complicated if you want to handle pointers and references (you should probably not);

  • you'll have to add code for serialization/deserialization of all the types you're using.

Serialization

On way to handle the serialization, would be to use generic programming for serialization/deserialization:

template<class T> class Type {};
typedef std::vector<char> Buffer;

// I'm clearly not claiming this would be efficient, but it gives
// the idea. In pratice, you might want to consume some streaming I/O
// API.
class Value {
Buffer buffer_;
public:
template<class T>
T get()
{
return deserialize(Type<T>(), buffer_);
}
template<class T>
Value& operator=(T const& x)
{
serialize(x, buffer_);
return *this;
}
};

inline std::uint32_t deserialize(Type<std::uint32_t>, Buffer const& buffer)
{
if (buffer.size() != sizeof(std::uint32_t))
throw std::runtime_error("Could not deserialize uint32");
std::uint32_t res;
memcpy(&res, buffer.data(), sizeof(std::uint32_t));
return be32toh(res);
}
inline void serialize(std::uint32_t value, Buffer const& buffer)
{
buffer.resize(sizeof(std::uint32_t));
value = htobe32(value);
memcpy(buffer.data(), &value, sizeof(std::uint32_t));
}

Another possibility is to use generic programming and let the Function do the serialization/deserialization.

What is the best type for a callable object in a template method?

In general, I do not like passing callable objects by const reference, because it is not that flexible (e.g. it cannot be used on mutable lambdas). I suggest to pass them by value. If you check the stl algorithms implementation, (e.g. for std::for_each), all of the callable objects are passed by value as well.

Doing this, the users are still able to use std::ref(func) or std::cref(func) to avoid unnecessary copying of the callable object (using reference_wrapper), if desired.

Are pointer to member functions not callable object

What i am asking is that whether pointer to member function are callable object according to the standard.

Yes, a pointer to a member function is a callable object according to the standard.

From func.def#4:

A callable object is an object of a callable type.

And from func.def#3:

A callable type is a function object type ([function.objects]) or a pointer to member.

Thus, the highlighted part of the quoted statement that says "a pointer to member is not a callable object" from C++ Primer is incorrect according the standard.

Inferring the call signature of a lambda or arbitrary callable for make_function

I've come up with a fairly nasty non-library solution, using the fact that lambdas have operator():

template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

template<typename T>
struct get_signature_impl { using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;

template<typename F> using make_function_type = std::function<get_signature<F>>;
template<typename F> make_function_type<F> make_function(F &&f) {
return make_function_type<F>(std::forward<F>(f)); }

Any ideas where this can be simplified or improved? Any obvious bugs?

Purpose of perfect forwarding for Callable argument in invocation expression?

For the same purpose as for arguments: so when Func::operator() is a ref-qualified:

struct Functor
{
void operator ()() const & { std::cout << "lvalue functor\n"; }
void operator ()() const && { std::cout << "rvalue functor\n"; }
};

Demo

Accept any kind of callable and also know argument type

Here's an example that will work for most callables including functors and lambdas (although not for generic functors as @Yakk demonstrated in a comment on the question).

The code can also be useful when determining return type and multiple arguments.

template <typename T>
struct func_traits : public func_traits<decltype(&T::operator())> {};

template <typename C, typename Ret, typename... Args>
struct func_traits<Ret(C::*)(Args...) const> {
using result_type = Ret;

template <std::size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};

template <typename T>
void option(T&& t) {
using traits = func_traits<typename std::decay<T>::type>;

using return_t = typename traits::result_type; // Return type.
using arg0_t = typename traits::template arg<0>::type; // First arg type.

// Output types.
std::cout << "Return type: " << typeid(return_t).name() << std::endl;
std::cout << "Argument type: " << typeid(arg0_t).name() << std::endl;
}

To add support for regular functions add a specialization e.g.

template <typename Ret, typename... Args>
struct func_traits<Ret(*)(Args...)> { /* ... */ }

More useful info: Is it possible to figure out the parameter type and return type of a lambda?



Related Topics



Leave a reply



Submit