How to write a variadic-template function with objects as parameters? - c++

Consider the following functions (it uses the CSV parser library from ben-strasser (github))
void col1(const std::string &fn, Base *v0)
{
io::CSVReader<2> in(fn);
in.read_header(io::ignore_extra_column, "epoch", v0->column);
double ign;
while (in.read_row(ign, v0->value)) {
v0->process();
}
}
void col2(const std::string &fn, Base *v0, Base *v1)
{
io::CSVReader<3> in(fn);
in.read_header(io::ignore_extra_column, "epoch", v0->column, v1->column);
double ign;
while (in.read_row(ign, v0->value, v1->value)) {
v0->process();
v1->process();
}
}
This function processes the value in column 2 of a CSV-file. v0 of type Base * contains the member value which is filled by read_row and is processed in the process-method. Base is an interface-class of calculation methods (for exemple: one is Max, another one is MinMaxAvg).
How could I rewrite this function to accept any number of Base * arguments in order to process multiple columns?
read_header and read_row are variadic-template function and thus can accept any number of arguments, but they only work with scalars.
How do I expand/unpack the variadic-argument so that it calls or uses a member?
I tried some things, reading some examples, but I'm unable to create something which works, here is my current/ridicules code:
template<unsigned int COL>
void func(const std::string &fn, Base &... values)
{
io::CSVReader<COL> in(fn);
// that's it :-(
}

Some well-placed pack expansions will work dandy:
template <class... Bases>
void col(const std::string &fn, Bases *... bases)
{
io::CSVReader<sizeof...(Bases) + 1u> in(fn);
in.read_header(io::ignore_extra_column, "epoch", bases->column...);
double ign;
while (in.read_row(ign, bases->value...)) {
// Awful C++11 arbitrary expansion trick
int dum[]{ 0, (void(
bases->process()
), 0)... };
(void) dum;
// Alternative, sweet and beautiful C++17 fold expression
// (void)(bases->process(), ...);
}
}

Two steps: first, we need to extend our function as desired:
template <typename ... Bases>
void f(std::string const& s, Bases* ... values)
{
io::CSVReader<sizeof...(Bases) + 1> in(s);
in.read_header(io::ignore_extra_column, "epoch", values->column ...);
double ign;
while(in.read_row(ign, values->value ...))
{
/* see below! */ process(values...);
}
}
So far no problem, read_header and read_row are variadic templates, so fine. Calling the member function was a little tricky, however - have a look at the call to the (yet unknown) process function above. Keyword compile time recursion (101010's answer), here we go:
void process()
{ }
template <typename ... Bases>
void process(Base* b, Bases* ... values)
{
b->process();
process(values ...);
}
Define these two functions before the template function, and it works...
Edit: Stealing sizeof...(Bases) + 1 from J.Doe...

Use pack expansion operator ... to unpack your variadic arguments.
template<typename... T> void nop(T&&...) { }
template<typename... Bases>
void func(const std::string &fn, Bases&&... bases)
{
io::CSVReader<sizeof...(Bases) + 1> in(fn);
in.read_header(io::ignore_extra_column, "epoch", bases->column...);
double ign;
while (in.read_row(ign, bases->value...)) {
// multiple ways to call process on all values
// prettier with C++17 stuff it seems
nop((bases->process(), 0)...);
}
}

With variadic templates you must materialize some short of compile time recursion:
template<unsigned int COL>
void func(const std::string &fn, Base &v) {
...
}
template<unsigned int COL, typename... Args>
void func(const std::string &fn, Base &v, Args&&... args) {
...
func<COL>(fn, std::forward<Args>(args)...);
}

compilable example (you will need to fill in the code for reading from the csv file and writing to each target):
#include <string>
#include <cstdint>
#include <utility>
#include <tuple>
template<class Function, class...Ts>
void for_all(Function f, Ts&&...ts)
{
using expand = int[];
void(expand{0,
(f(std::forward<Ts>(ts)), 0)...
});
}
// some mocked io library
namespace io
{
template<std::size_t MaxRows>
struct CSVReader
{
CSVReader(const std::string& s)
{
}
template<class...Targets>
void read_headers(std::tuple<Targets...>& target)
{
read_headers_impl(std::make_index_sequence<sizeof...(Targets)>(), target);
}
template<class...Targets>
void read_row(std::tuple<Targets...>& targets)
{
read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets);
}
// support for std::tie
template<class...Targets>
void read_row(const std::tuple<Targets...>& targets)
{
read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets);
}
private:
template<std::size_t...Is, class Tuple>
void read_headers_impl(std::index_sequence<Is...>, Tuple& target)
{
for_all([](auto&& target) {
// read the header and assign it to target here
}, std::get<Is>(target)...);
}
template<std::size_t...Is, class Tuple>
void read_values_impl(std::index_sequence<Is...>, Tuple& target)
{
for_all([](auto&& target) {
// read the values and assign it to target here
}, std::get<Is>(target)...);
}
};
}
struct Base
{
std::string& value();
void process();
};
template<std::size_t N, class T, class Current = std::tuple<>> struct n_tuple;
template<std::size_t N, class T> using n_tuple_t = typename n_tuple<N, T>::type;
template<std::size_t N, class T, class Current>
struct n_tuple
{
using type = std::conditional_t<
N == std::tuple_size<Current>::value,
Current,
decltype(std::tuple_cat(std::declval<Current>(), std::declval<n_tuple_t<N-1, T>>()))
>;
};
template<class...Bases>
void col_n(const std::string &fn, Bases&...bases)
{
constexpr std::size_t column_count = sizeof...(Bases) + 1;
io::CSVReader<column_count> in(fn);
using headers_type = n_tuple_t<column_count, std::string>;
auto headers = headers_type();
in.read_headers(headers);
double ign;
auto value_refs = std::tie(ign, bases.value()...);
while (in.read_row(value_refs)) {
// now we only want to process each base
for_all([](auto&& base) {
base.process();
}, bases...);
}
}

Related

Recreate function signature and call via template packs in C++

I have C code that I want to rewrite in C++. The C code is part of a interpreter where the functions are defined in C however the actual call is from the interpreted source. Basically what it does is listed below:
#include <vector>
void f1(int a0) { }
void f2(int a0,int a1) { }
void f3(int a0,int a1,int a2) { }
void f4(int a0,int a1,int a2,int a3) { }
struct m {
void *p;
int c;
};
std::vector<m> ma;
int addfunc(void *p, int c) {
int i = ma.size();
ma.push_back({p,c});
return i;
}
void call(int idx, int *stack) {
switch (ma[idx].c) {
case 1:
((void (*)(int))ma[idx].p) (stack[0]);
break;
case 2:
((void (*)(int,int))ma[idx].p) (stack[0],stack[1]);
break;
case 3:
((void (*)(int,int,int))ma[idx].p) (stack[0],stack[1],stack[2]);
break;
case 4:
((void (*)(int,int,int,int))ma[idx].p) (stack[0],stack[1],stack[2],stack[3]);
break;
}
}
int main (void) {
int stack[5] = { 0,1,2,3,4 };
/* define */
int i1 = addfunc((void*)f1, 1);
int i2 = addfunc((void*)f2, 2);
int i3 = addfunc((void*)f3, 3);
int i4 = addfunc((void*)f4, 4);
/* call */
call(i1,stack);
call(i2,stack);
call(i3,stack);
call(i4,stack);
}
The addfunc creates a callable object specified by a function pointer and a signature, because the arguments are of the same type int only a count argument for the number of arguments is needed.
When I call a function I specify the function object's index and a stack. The actual c-call is decoded via the argument count and typecasted, the call arguments are taken from the stack.
How can I rewrite the addfunc and call functions as templates objects in C++? How can I use template packs to count the number of arguments for the given function and regenerate the call to the function?
How can I get rid of the switch statement and the function pointer typecast? I have seen that luawrapper's Binder class does something similar. However the code is quite complicated. In my case the arguments are all of the same type.
In the end I want to do something like (pseudocode):
vector<meth> ma;
...
int i0 = addfunc([](int a) { });
int i1 = addfunc([](int a,int b) { });
int i2 = addfunc([](int a,int b,int b) { });
int i3 = addfunc([](int a,int b,int c,int c) { });
...
ma[i0](stack);
ma[i1](stack);
ma[i2](stack);
ma[i3](stack);
Well, if they're just C functions, why not overload on the function pointer type?
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int)) {
return [f](std::array<int, 5> const& a) { f(a[0]); };
}
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int,int)) {
return [f](std::array<int, 5> const& a) { f(a[0], a[1]); };
}
// repeat for all necessary arities
Then create std::vector<std::function<void(std::array<int, 5>)>> and push back all your functions. It's easy, doesn't require any templates and will work reasonably well. It introduces the overhead of std::function, though.
You could get rid of that by introducing your own callable type (n of them), that would correspond to the overloads above, provide an operator() and store appropriate function type inside.
Live example.
Unfortunately, you won't be able to make a completely generic solution, as there is no way to type-erase arity.
One way you can simplify things would be to create a set of wrappers for your functions, each wrapper accepting a stack*, and calling implementation functions with arguments from said stack.
Than you do not need typecasts at all and a simple function pointer (to approriate wrapper) would do (no even need to type-erase).
I propose a C++17 solution (simplified following a Jarod42's observation: thanks) that I suppose is over-complicated.
But I find it funny...
First: a struct that, given (as template parameters) a type and a unsigned number, define a type as the type received.
template <typename T, std::size_t>
struct getType
{ using type = T; };
It's used to convert a variadic template list of numbers in a sequence of types (ints, in the following example) of the same length.
Next: a template type that register (setFunc()) and exec (callFunc()) a function returning void and a sequence of ints length as the first template parameter.
template <std::size_t N, typename = std::make_index_sequence<N>>
struct frHelper;
template <std::size_t N, std::size_t ... Is>
struct frHelper<N, std::index_sequence<Is...>>
{
using fnPnt_t = void(*)(typename getType<int, Is>::type...);
fnPnt_t fp = nullptr;
void setFunc (fnPnt_t fp0)
{ fp = fp0; }
void callFunc (std::array<int, sizeof...(Is)> const & a)
{ if ( fp ) fp(a[Is]...); }
};
Last: a template struct that inherit from a variadic list of preceding structs and enable (using) the setFunc() and the callFunc() members.
template <std::size_t N, typename = std::make_index_sequence<N>>
struct funcRegister;
template <std::size_t N, std::size_t ... Is>
struct funcRegister<N, std::index_sequence<Is...>>
: public frHelper<Is>...
{
using frHelper<Is>::setFunc...;
using frHelper<Is>::callFunc...;
};
Use.
First you have to declare an object of type funcRegister<N> where N is the max number of integer received from your functions plus one. So if you want to use f4(), so four integers, you have to declare
funcRegister<5u> fr;
Then you have to register the functions
fr.setFunc(f1);
fr.setFunc(f2);
fr.setFunc(f3);
fr.setFunc(f4);
and, given some std::array<int, N> of the right size, you can call the registered functions
std::array a1 { 1 };
std::array a2 { 1, 2 };
std::array a3 { 1, 2, 3 };
std::array a4 { 1, 2, 3, 4 };
fr.callFunc(a1); // call f1
fr.callFunc(a2); // call f2
fr.callFunc(a3); // call f3
fr.callFunc(a4); // call f4
The following is a full compiling C++17 example
#include <array>
#include <utility>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <std::size_t N, typename = std::make_index_sequence<N>>
struct frHelper;
template <std::size_t N, std::size_t ... Is>
struct frHelper<N, std::index_sequence<Is...>>
{
using fnPnt_t = void(*)(typename getType<int, Is>::type...);
fnPnt_t fp = nullptr;
void setFunc (fnPnt_t fp0)
{ fp = fp0; }
void callFunc (std::array<int, sizeof...(Is)> const & a)
{ if ( fp ) fp(a[Is]...); }
};
template <std::size_t N, typename = std::make_index_sequence<N>>
struct funcRegister;
template <std::size_t N, std::size_t ... Is>
struct funcRegister<N, std::index_sequence<Is...>>
: public frHelper<Is>...
{
using frHelper<Is>::setFunc...;
using frHelper<Is>::callFunc...;
};
void f1(int) { std::cout << "f1 called" << std::endl; }
void f2(int,int) { std::cout << "f2 called" << std::endl;}
void f3(int,int,int) { std::cout << "f3 called" << std::endl;}
void f4(int,int,int,int) { std::cout << "f4 called" << std::endl;}
int main()
{
funcRegister<5u> fr;
fr.setFunc(f1);
fr.setFunc(f2);
fr.setFunc(f3);
fr.setFunc(f4);
std::array a1 { 1 };
std::array a2 { 1, 2 };
std::array a3 { 1, 2, 3 };
std::array a4 { 1, 2, 3, 4 };
fr.callFunc(a1);
fr.callFunc(a2);
fr.callFunc(a3);
fr.callFunc(a4);
}
Here is luawrapper's code extracted to be applied the above case. This is more for completion as for #Jerod42's code is preferable.
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <functional>
#include <vector>
template<typename T> struct tag {};
template<typename TFunctionObject, typename TFirstParamType>
struct Binder {
TFunctionObject function;
TFirstParamType param;
template<typename... TParams>
auto operator()(TParams&&... params)
-> decltype(function(param, std::forward<TParams>(params)...))
{
return function(param, std::forward<TParams>(params)...);
}
};
template<typename TCallback>
static void readIntoFunction(int *stack, TCallback&& callback)
{
callback();
}
template<typename TCallback, typename TFirstType, typename... TTypes>
static void readIntoFunction(int *stack, TCallback&& callback, tag<TFirstType>, tag<TTypes>... othersTags)
{
Binder<TCallback, const TFirstType&> binder{ callback, *stack };
return readIntoFunction(++stack, binder, othersTags...);
}
/* decompose arguments */
template<typename TFunctionType, typename... TOtherParams>
std::function<void(int*)> _addfunc(TFunctionType f, tag<void (*)(TOtherParams...)>) {
return std::function<void(int*)>([f](int *stack) {
readIntoFunction(stack, f, tag<TOtherParams>{}...);
});
}
template<typename TFunctionType>
std::function<void(int*)> addfunc(TFunctionType fn)
{
typedef typename std::decay<TFunctionType>::type RealFuncSig;
return _addfunc(std::move(fn), tag<RealFuncSig>{} );
}
void f1(int a0) { std::cout << a0 << std::endl; }
void f2(int a0, int a1) { std::cout << a0 << a1 << std::endl; }
int main() {
int stack[5] = { 0,1,2,3,4 };
auto a0 = addfunc(&f1);
auto a1 = addfunc(&f2);
a0(stack);
a1(stack);
}
you can use std:function as the parameter of the addfun() and also std::bind

C++11 std::conditional at runtime?

I'm looking to do something like this:
void func(void *data, const int dtype)
{
typedef typename std::conditional<dtype==0,float,double>::type DataType;
funcT((DataType *)data);
return;
}
This will not compile because dtype needs to be known at compile time. I'm trying to avoid using a switch statement, because I have 8 data types I am working with, with many functions such as the one above, being called from Python via ctypes.
Is there a way something like std::conditional can done during run time, making use of the dtype identifier passed in?
All types must be resolved at compile time. So no type, can ever depend on a runtime parameter to a function. The way to handle something like this is basically to build a visiting mechanism, once, and then you can reuse it. Basically, something like this:
template <class F>
void visit_data(void* data, const int dtype, F f) {
switch (dtype)
case 0: f(*static_cast<float*>(data));
case 1: f(*static_cast<double*>(data));
}
Now you can implement functions by writing visitors:
struct func_impl {
void operator()(float&) { ... }
void operator()(double&) { ... }
};
Your visitor can also use generic code:
struct func_impl2 {
template <class T>
void operator()(T&) { ... }
};
Then you can write your function by leveraging the visitor:
void func(void* data, const int dtype) {
visit_data(data, dtype, func_impl{});
}
The switch case over your list of types will only appear once in your entire codebase. If you add a new type, any visitor that doesn't handle it will give a compile time error if used.
You can also use lambdas to do it inline with a helper function or two; especially useful in 14 where you have generic lambdas.
If you can use C++17 it can be solved with a std::visitor and std::variant like so:
using var_t = std::variant<float, double>;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
void func(var_t arg) {
std::visit(overloaded {
[](float arg) { foo_float(arg); },
[](double arg) { foo_double(arg); },
}, arg);
}
I'll start with a c++14 answer then detail how to downgrade c++11.
Suppose you have a list of types:
template<class...>
struct types{using type=types;};
const types<int, double, char> supported_types;
Next we write some utility functions
template<std::size_t I, class...Ts>
using get_type = std::decay_t<decltype(std::get<I>(std::declval<std::tuple<Ts...>&>()))>;
template<std::size_t I, class Types>
struct type_at_helper;
template<std::size_t I, class...Ts>
struct type_at_helper<I, types<Ts...>>{
using type=get_type<I,Ts...>;
};
template<std::size_t I, class Types>
using type_at = typename type_at_helper<I,Types>::type;
Now, type_at<2, decltype(supperted_types)> is char.
namespace helper {
template<class F>
using invoker = void(*)(F&&, void*);
template<class F, class Types, std::size_t I>
invoker<F> get_invoker() {
return [](F&& f, void* pdata) {
std::forward<F>(f)( static_cast<type_at<I, Types>*>(pdata) );
};
}
template<class F, class Types, std::size_t...Is>
void dispatch( F&& f, void* data, unsigned type_index, std::index_sequence<Is...>, Types ={} ) {
using pF=std::decay_t<F>*;
using invoker = void(*)(pF, void*);
static const invoker table[]={
get_invoker<F, Types, Is>()...
};
table[type_index]( std::forward<F>(f), data );
}
}
template<class F, class...Ts>
void dispatch( F&& f, void* data, unsigned type_index, types<Ts...> {} ) {
details::dispatch( std::forward<F>(f), data, type_index, std::make_index_sequence<sizeof...(Ts)>{}, types<Ts...>{} );
}
and done.
The downgrade to c++11 simply write make_index_sequence and index_sequence. Here is a high quality one, but there are easier ones out there.
Is there a way something like std::conditional can done during run time, making use of the dtype identifier passed in?
No, there isn't. A run time value cannot be used to make type based decisions at compile type.
Given your post, the simplest solution is to use an if statement.
void func(void *data, const int dtype)
{
if ( dtype == 0 )
{
funcT(static_cast<float*>(data));
}
else
{
funcT(static_cast<double*>(data));
}
}
To be able to deal with lots of such functions, I would recommend using std::map<int, std::function<void(void*)>>.
Here's a simple program that compiles and builds for me.
#include <map>
#include <functional>
void funcT(float* data)
{
}
void funcT(double* data)
{
}
struct MyType {};
void funcT(MyType* data)
{
}
void func(void *data, const int dtype)
{
std::map<int, std::function<void(void*)>> functions =
{
{0, [](void* in) {funcT(static_cast<float*>(in));}},
{1, [](void* in) {funcT(static_cast<double*>(in));}},
// ...
{7, [](void* in) {funcT(static_cast<MyType*>(in));}}
};
if ( functions[dtype] != nullptr )
{
functions[dtype](data);
}
}
int main(){}
One advantage of using the lambda functions is that you are free to call differently named functions for the various types. For example, you have option of using:
void foo(MyType* data) {}
and
{7, [](void* in) {foo(static_cast<MyType*>(in));}}
My solution to the problem would be a generic selectFunc() function which would select a function from a provided function set FS based on dtype, and return it:
using FuncType = void(*)(void*);
template<typename FS>
FuncType selectFunc(int dtype);
The function set would be a class with static handle() methods which would accept different types and a static fallback() method which would be called if dtype is not valid.
Example Usage:
struct FuncSet
{
static void fallback() {};
static void handle(float*) {};
static void handle(double*) {};
};
void func(void *data, int dtype)
{
// select a function from FuncSet based on dtype:
auto f = selectFunc<FuncSet>(dtype);
// invoke the selected function with the provided data:
f(data);
// note, two lines above could be combined into one line
}
Implementation:
// Static method which would call the correct FS::handle() method
template<typename FS, typename T>
struct Helper
{
static void dispatch(void *data) { FS::handle(static_cast<T*>(data)); }
};
// Static method which would call FS::fallback()
template<typename FS>
struct Helper<FS, void>
{
static void dispatch(void*) { FS::fallback(); }
};
template<typename FS>
FuncType selectFunc(int dtype)
{
switch ( dtype ) {
case 0: return &Helper<FS, float>::dispatch;
case 1: return &Helper<FS, double>::dispatch;
// ... add other types here ...
default: return &Helper<FS, void>::dispatch; // call fallback()
}
}

Get function with generic return type

I try to implement a data structure that comprises multiple name-value pairs where values may differ in their type:
template< typename T >
struct name_value_pair
{
std::string name;
T value;
};
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
/* type of value */ get_value( std::string n )
{
// return the value that the element in
// _name_value_pairs with name "n" comprises
}
private:
std::tuple<Ts...> _name_value_pairs:
};
Unfortunately, I have no idea how to implement the get function.
A workaround would be to state names as integers instead of strings and use an implementation according to std::get but this no option here: the input type of get has to be a string.
Has anyone an idea?
Firstly have in mind you cannot do what you want directly. C++ is a strongly typed language so type of function result must be known at compile time. So of course if the string you pass to the getter is known at runtime you're not able to dispatch function at compile time to let compiler deduce appropriate result type. But when you accept that you need type-erasure to erase the getter result type you could make use of e.g. boost::variant to deal with your problem. C++14 example (using boost, since c++17 variant should be available in std):
#include <boost/variant.hpp>
#include <utility>
#include <iostream>
#include <tuple>
template< typename T >
struct name_value_pair
{
using type = T;
std::string name;
T value;
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct getter;
template <std::size_t N, std::size_t... Is>
struct getter<N, std::index_sequence<Is...>> {
template <class Val, class Res>
void setRes(Val &val, Res &res, std::string &s) {
if (val.name == s)
res = val.value;
}
template <class Tup>
auto operator()(Tup &tuple_vals, std::string &s) {
boost::variant<typename std::tuple_element<Is, Tup>::type::type...> result;
int helper[] = { (setRes(std::get<Is>(tuple_vals), result, s), 1)... };
(void)helper;
return result;
}
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct setter;
template <std::size_t N, std::size_t... Is>
struct setter<N, std::index_sequence<Is...>> {
template <class Val, class SVal>
std::enable_if_t<!std::is_same<SVal, typename Val::type>::value> setVal(Val &, std::string &, const SVal &) { }
template <class Val>
void setVal(Val &val, std::string &s, const typename Val::type &sval) {
if (val.name == s)
val.value = sval;
}
template <class Tup, class Val>
auto operator()(Tup &tuple_vals, std::string &s, const Val &val) {
int helper[] = { (setVal(std::get<Is>(tuple_vals), s, val), 1)... };
(void)helper;
}
};
template <class T, class Res>
using typer = Res;
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
auto get_value( std::string n )
{
return getter<sizeof...(Ts)>{}(_name_value_pairs, n);
}
template <class T>
void set_value( std::string n, const T& value) {
setter<sizeof...(Ts)>{}(_name_value_pairs, n , value);
}
void set_names(typer<Ts, std::string>... names) {
_name_value_pairs = std::make_tuple(name_value_pair<Ts>{names, Ts{}}...);
}
private:
std::tuple<name_value_pair<Ts>...> _name_value_pairs;
};
int main() {
tuple_of_name_value_pairs<int, float, double> t;
t.set_names("abc", "def", "ghi");
t.set_value("abc", 1);
t.set_value("def", 4.5f);
t.set_value("ghi", 5.0);
std::cout << t.get_value("def") << std::endl;
}
[live demo]
I'm sure you'll be able to optimise the code (e.g. make use of move semantics/perfect forwarding, etc.). This is only to present you how to get your implementation started.

trouble with specialising template template parameters

I often use the following construction for converting run-time (dynamic) arguments into compile-time (static) arguments
namespace Foo {
enum struct option { A,B,C,D,E,F };
template<template<option> class Func, typename... Args>
auto Switch(option opt, Args&&...args)
-> decltype(Func<option::A>::act(std::forward<Args>(args)...))
{
switch(opt) {
case option::A : return Func<option::A>::act(std::forward<Args>(args)...);
case option::B : return Func<option::B>::act(std::forward<Args>(args)...);
// etc.
}
}
For example
template<Foo::option>
void compile_time(std::string const&); // given
namespace {
template<Foo::option Opt>
struct Helper {
static void act(std::string const&str) { compile_time<Opt>(str); }
};
}
void run_time_arg(Foo::option opt, std::string const&str)
{
Switch<CompileTimeArg>(opt,str);
}
So far so good. But now I have another template argument and want also blah() to have that same template argument. That is, conceptually I want to
template<int, Foo::option>
void compile_time(std::string const&); // given
namespace {
template<int Bar, Foo::option Opt>
struct Helper {
static void act(std::string const&str) { compile_time<Bar,Opt>(str); }
};
}
template<int Bar>
void blah(Foo::option opt, std::string const&str)
{
template<Foo::option Opt> using BarHelper = Helper<Bar,Opt>;
Switch<BarHelper>(opt, str);
}
but, of course, that is not allowed (a template within block scope in function blah()). So what is the correct solution?
Note that I can put everything within an auxiliary class template
namespace {
template<int Bar>
struct Auxiliary
{
template<Foo::option Opt> using BarHelper = Helper<Bar,Opt>;
static void blah(Foo::option opt, std::string const&str)
{ Switch<BarHelper>(opt, str); }
};
}
template<int Bar>
void blah(Foo::option opt, std::string const&str)
{ Auxiliary<Bar>::blah(opt, str); }
But that's clumsy and unsatisfying. Is there an alternative or better solution?
I tried this:
template<typename X, typename Y, X x, template<X,Y> class C>
struct specialise {
template<Y y> using special = C<x,y>;
};
template<int Bar>
void blah(Foo::option opt, std::string const&str)
{
using Aux = specialise<int, Foo::option, Bar, Helper>
Switch<Aux::special>(opt, str); }
}
but gcc (5.1.0) complains that S::special is parsed as a non-type while instantiation yields a type ... which is wrong (I think): instantiation yields a template (anyway inserting typename as suggested doesn't help). So what's wrong and/or how to do this correct/better?
The keyword to add is not typename as it is not a type, but template.
So, the call should be
template<int Bar>
void blah(Foo::option opt, std::string const& str)
{
using Aux = specialise<int, Foo::option, Bar, Helper>
Switch<Aux::template special>(foo, ptr, str);
}
Live Demo

What is a good way to register functions for dynamic invocation in C++?

In my current setup, I have a
typedef std::function<void (MyClass&, std::vector<std::string>) MyFunction;
std::map<std::string, MyFunction> dispatch_map;
And I register my functions in it with a macro. However, I have a problem with this: the parameters are passed as a vector of strings, which I have to convert inside the functions. I would rather do this conversion outside the functions, at the dispatcher level. Is this possible? The function signatures are known at compile time, and never change at run time.
You can get pretty far with variadic templates and some template/virtual techniques. With the following codes, you'll be able to do something like:
std::string select_string (bool cond, std::string a, std::string b) {
return cond ? a : b;
}
int main () {
Registry reg;
reg.set ("select_it", select_string);
reg.invoke ("select_it", "1 John Wayne"));
reg.invoke ("select_it", "0 John Wayne"));
}
output:
John
Wayne
Full implementation:
These codes are exemplary. You should optimize it to provide perfect forwarding less redundancy in parameter list expansion.
Headers and a test-function
#include <functional>
#include <string>
#include <sstream>
#include <istream>
#include <iostream>
#include <tuple>
std::string select_string (bool cond, std::string a, std::string b) {
return cond ? a : b;
}
This helps us parsing a string and putting results into a tuple:
//----------------------------------------------------------------------------------
template <typename Tuple, int Curr, int Max> struct init_args_helper;
template <typename Tuple, int Max>
struct init_args_helper<Tuple, Max, Max> {
void operator() (Tuple &, std::istream &) {}
};
template <typename Tuple, int Curr, int Max>
struct init_args_helper {
void operator() (Tuple &tup, std::istream &is) {
is >> std::get<Curr>(tup);
return init_args_helper<Tuple, Curr+1, Max>() (tup, is);
}
};
template <int Max, typename Tuple>
void init_args (Tuple &tup, std::istream &ss)
{
init_args_helper<Tuple, 0, Max>() (tup, ss);
}
This unfolds a function pointer and a tuple into a function call (by function-pointer):
//----------------------------------------------------------------------------------
template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper;
template <int Max, typename Ret, typename ...Args>
struct unfold_helper<Max, Max, Ret, Args...> {
template <typename Tuple, typename ...Params>
Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
{
return fun (params...);
}
};
template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper {
template <typename Tuple, typename ...Params>
Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
{
return unfold_helper<ParamIndex+1, Max, Ret, Args...> ().
unfold(fun, tup, params..., std::get<ParamIndex>(tup));
}
};
template <typename Ret, typename ...Args>
Ret unfold (Ret (*fun) (Args...), std::tuple<Args...> tup) {
return unfold_helper<0, sizeof...(Args), Ret, Args...> ().unfold(fun, tup);
}
This function puts it together:
//----------------------------------------------------------------------------------
template <typename Ret, typename ...Args>
Ret foo (Ret (*fun) (Args...), std::string mayhem) {
// Use a stringstream for trivial parsing.
std::istringstream ss;
ss.str (mayhem);
// Use a tuple to store our parameters somewhere.
// We could later get some more performance by combining the parsing
// and the calling.
std::tuple<Args...> params;
init_args<sizeof...(Args)> (params, ss);
// This demondstrates expanding the tuple to full parameter lists.
return unfold<Ret> (fun, params);
}
Here's our test:
int main () {
std::cout << foo (select_string, "0 John Wayne") << '\n';
std::cout << foo (select_string, "1 John Wayne") << '\n';
}
Warning: Code needs more verification upon parsing and should use std::function<> instead of naked function pointer
Based on above code, it is simple to write a function-registry:
class FunMeta {
public:
virtual ~FunMeta () {}
virtual boost::any call (std::string args) const = 0;
};
template <typename Ret, typename ...Args>
class ConcreteFunMeta : public FunMeta {
public:
ConcreteFunMeta (Ret (*fun) (Args...)) : fun(fun) {}
boost::any call (std::string args) const {
// Use a stringstream for trivial parsing.
std::istringstream ss;
ss.str (args);
// Use a tuple to store our parameters somewhere.
// We could later get some more performance by combining the parsing
// and the calling.
std::tuple<Args...> params;
init_args<sizeof...(Args)> (params, ss);
// This demondstrates expanding the tuple to full parameter lists.
return unfold<Ret> (fun, params);
}
private:
Ret (*fun) (Args...);
};
class Registry {
public:
template <typename Ret, typename ...Args>
void set (std::string name, Ret (*fun) (Args...)) {
funs[name].reset (new ConcreteFunMeta<Ret, Args...> (fun));
}
boost::any invoke (std::string name, std::string args) const {
const auto it = funs.find (name);
if (it == funs.end())
throw std::runtime_error ("meh");
return it->second->call (args);
}
private:
// You could use a multimap to support function overloading.
std::map<std::string, std::shared_ptr<FunMeta>> funs;
};
One could even think of supporting function overloading with this, using a multimap and dispatching decisions based on what content is on the passed arguments.
Here's how to use it:
int main () {
Registry reg;
reg.set ("select_it", select_string);
std::cout << boost::any_cast<std::string> (reg.invoke ("select_it", "0 John Wayne")) << '\n'
<< boost::any_cast<std::string> (reg.invoke ("select_it", "1 John Wayne")) << '\n';
}
If you can use boost, then here's an example of what I think you're trying to do ( although might work with std as well, I stick with boost personally ):
typedef boost::function<void ( MyClass&, const std::vector<std::string>& ) MyFunction;
std::map<std::string, MyFunction> dispatch_map;
namespace phx = boost::phoenix;
namespace an = boost::phoenix::arg_names;
dispatch_map.insert( std::make_pair( "someKey", phx::bind( &MyClass::CallBack, an::_1, phx::bind( &boost::lexical_cast< int, std::string >, phx::at( an::_2, 0 ) ) ) ) );
dispatch_map["someKey"]( someClass, std::vector< std::string >() );
However, as this sort of nesting quickly becomes fairly unreadable, it's usually best to either create a helper ( free function, or better yet a lazy function ) that does the conversion.
If I understand you correctly, you want to register void MyClass::Foo(int) and void MyClass::Bar(float), accepting that there will be a cast from std::string to int or float as appropriate.
To do this, you need a helper class:
class Argument {
std::string s;
Argument(std::string const& s) : s(s) { }
template<typename T> operator T { return boost::lexical_cast<T>(s); }
};
This makes it possible to wrap both void MyClass::Foo(int) and void MyClass::Bar(float) in a std::function<void(MyClass, Argument))>.
Interesting problme. This is indeen not trivial in C++, I wrote a self-contained implementation in C++11. It is possible to do the same in C++03 but the code would be (even) less readable.
#include <iostream>
#include <sstream>
#include <string>
#include <functional>
#include <vector>
#include <cassert>
#include <map>
using namespace std;
// string to target type conversion. Can replace with boost::lexical_cast.
template<class T> T fromString(const string& str)
{ stringstream s(str); T r; s >> r; return r; }
// recursive construction of function call with converted arguments
template<class... Types> struct Rec;
template<> struct Rec<> { // no parameters
template<class F> static void call
(const F& f, const vector<string>&, int) { f(); }
};
template<class Type> struct Rec< Type > { // one parameter
template<class F> static void call
(const F& f, const vector<string>& arg, int index) {
f(fromString<Type>(arg[index]));
}
};
template<class FirstType, class... NextTypes>
struct Rec< FirstType, NextTypes... > { // many parameters
template<class F> static void call
(const F& f, const vector<string>& arg, int index) {
Rec<NextTypes...>::call(
bind1st(f, fromString<FirstType>(arg[index])), // convert 1st param
arg,
index + 1
);
}
};
template<class... Types> void call // std::function call with strings
(const function<void(Types...)>& f, const vector<string>& args) {
assert(args.size() == sizeof...(Types));
Rec<Types...>::call(f, args, 0);
}
template<class... Types> void call // c function call with strings
(void (*f)(Types...), const vector<string>& args) {
call(function<void(Types...)>(f), args);
}
// transformas arbitrary function to take strings parameters
template<class F> function<void(const vector<string>&)> wrap(const F& f) {
return [&] (const vector<string>& args) -> void { call(f, args); };
}
// the dynamic dispatch table and registration routines
map<string, function<void(const vector<string>&)> > table;
template<class F> void registerFunc(const string& name, const F& f) {
table.insert(make_pair(name, wrap(f)));
}
#define smartRegister(F) registerFunc(#F, F)
// some dummy functions
void f(int x, float y) { cout << "f: " << x << ", " << y << endl; }
void g(float x) { cout << "g: " << x << endl; }
// demo to show it all works;)
int main() {
smartRegister(f);
smartRegister(g);
table["f"]({"1", "2.0"});
return 0;
}
Also, for performances, it's better to use unordered_map instead of map, and maybe avoid std::function overhead if you only have regular C functions. Of course this is only meaningful if dispatch time is significant compared to functions run-times.
No, C++ provides no facility for this to occur.