I'm trying to make a C++ function that accepts an unknown number of parameters total, but that they are always paired with specific types.
// logically, this is what the template Pair would be
// template<int, std::string> struct Pair {};
// desired:
// accept a const char * as a first parameter, and then in pairs ...
// integer, const char *
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, const Arguments& ... args) {
const unsigned numargs = sizeof...(Arguments);
// more magic would happen here with the parameters :)
return numargs;
}
int _tmain(int argc, _TCHAR* argv[])
{
// only string, [num, string] [num, string] should work
// desire that the syntax be as simple as shown, and not require
// extra classes to be created (like a Tuple) for each pair.
// this should work...
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
// this should not work, as it's not number, string
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
return 0;
}
I've looked at parameter packs (reference), but can't seem to apply the documentation I've found to my specific problem. I'd like to try to catch the problem at compile time if the parameters are not specified correctly.
The goal was to use a syntax that was free of template noise as much as possible as the "pairs" will always be this way (and the programmer will know that). So, we wanted to just have int, string (repeat).
Ideally, the solution would work with Visual Studio 2013's C++ compiler, but I'd accept any answer that works and demonstrates the current possible shortcomings of VS C++ related to this issue.
Appendix - More details
The code being written would ultimately be often read by tech-savvy, but not formally trained C/C++ programmers (like a technical support). So, we're trying to get it to be distraction free as much as possible. There can be 2-16 pairs of values ... so keeping it distraction free and just the data is desirable.
Here's one possibility. Class template Enforce recursively inherits from itself and applies static_assert on pairs of template arguments until the specialization is picked that doesn't do anything:
#include <type_traits>
#include <string>
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> : Enforce<T, Args...> {
static_assert( std::is_constructible<T, T2>::value, "Wrong T2!");
};
template<typename T>
struct Enforce<T> {
};
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
Enforce<std::string, Arguments...>();
}
int main()
{
onlyInPairs("this", 1, "works", 2, "fine");
//onlyInPairs("this", 1, "doesn't", 2, 3);
}
Instead of recursive inheritance, you can use recursive typedef instead. At least in gcc, that ought to compile faster and with less noise (warning about non-virtual destructor in base class, etc.).
EDIT:
Here's another version that ANDs the checks together and saves the result:
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> {
static const bool value =
std::is_constructible<T,T2>::value &&
Enforce<T, Args...>::value;
};
template<typename T>
struct Enforce<T> : std::true_type {
};
Now you can move the assert closer, inside onlyInPairs:
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
static_assert( Enforce<std::string, Arguments...>::value , "Wrong second arg..." );
}
What template noise do you speak of?
void onlyInPairs(std::initializer_list<std::pair<int, std::string>>&& pairs) {}
int main() {
onlyInPairs({
{1, "abc"},
{2, "def"},
{3, "foo"},
});
}
Use compile time recursion:
void processArgPairs() {
// to stop recursion
}
template <typename Arg1, typename Arg2, typename... Arguments>
void processArgPairs(Arg1 a, Arg2 b, Arguments&& ...args){
static_assert(std::is_constructible<int, Arg1>::value, "Wrong type of first argument - int expected");
static_assert(std::is_constructible<std::string, Arg2>::value, "Wrong type of second argument - string expected
processArgPairs(std::forward<Arguments>(args)...);
}
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, Arguments&& ... args) {
const unsigned numargs = sizeof...(Arguments);
processArgPairs(std::forward<Arguments>(args)...);
return numargs;
}
Something like this?
template <typename... Arguments>
unsigned int onlyInPairs(const std::string name, const Arguments& ... args)
{
const unsigned numargs = sizeof...(Arguments);
check(args...);
return numargs;
}
template <typename... Arguments>
void check(const int i, const std::string name, const Arguments& ... args)
{
check(args...);
}
void check(const int i, const std::string name)
{
}
int main()
{
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2); //compile error
return 0;
}
This is a fairly old-school solution: using is_convertible should be cleaner
#include <string>
template <typename... Args> struct EnforcePairsHelper;
// terminal case
template <> struct EnforcePairsHelper<> {
enum { size = 0 };
};
// multiple specializations for reliable matching:
// only the last is really required here
template <typename... ArgTail>
struct EnforcePairsHelper<int, const char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... ArgTail>
struct EnforcePairsHelper<int, char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <int N, typename... ArgTail>
struct EnforcePairsHelper<int, char [N], ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... Args> unsigned onlyInPairs (const std::string name,
const Args& ... args) {
const unsigned numargs = EnforcePairsHelper<Args...>::size;
// more magic would happen here with the parameters :)
return numargs;
}
int main() {
unsigned ok = onlyInPairs("ABC", 1, "DEF", 2, "HIJ");
// unsigned no = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
}
Related
The function 'Process' is taking a variable number of arguments of variable type. To handle different cases, I have successfully overloaded it like this:
// general case
template <typename ...Types>
void Process( const Types&... items )
// single T
template <typename T>
void Process( const T& t )
// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )
What I want to do - but can't - is the following: I need an overload for cases with any number of leading arguments of a types ATT<> followed by any number of NVP<> like this:
// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... atts, const NVP<BTypes>&... nvps )
At first you would think it should be 'easy' for a compiler to match this, if it can already do the other cases. There should be absolutely no ambiguity here!? However, the matching fails, no error messages, but the desired overload it is just ignored by the compiler.
Currently using VS2017 with /std:c++17
Notes:
1. It can, obviously, be done for one leading type ATT<T1> like this
// one leading Type ATT<T1>
template <typename T1, typename ...Types>
void Process( const ATT<T1>& a1, const Types&... remaining )
But for more than one, I need to do some ugly manual recursion. I really want to have the whole pack of leading ATT<...>.
2. I am aware that a leading parameter pack - of general types - always is ambiguous for matching, but for a specialization like ATT<ATypes>... no ambiguity should exist.
You could dispatch from the const Types&... overload based on if Types... matches ATT<T>..., NVP<U>....
The basic strategy here is finding the index of the last ATT<T>, forwarding everything as a tuple, then indexing with the appropriate index sequence to forward to another function where the ATT values and NVP values are in two tuples:
namespace detail {
template<class...>
struct get_split_index;
template<class T, class... Others>
struct get_split_index<T, Others...> {
static constexpr std::size_t i = -1;
};
template<class T, class... Others>
struct get_split_index<ATT<T>, Others...> {
static constexpr std::size_t next = get_split_index<Others...>::i;
static constexpr std::size_t i = next == -1 ? -1 : next + 1u;
};
template<class T, class... Others>
struct get_split_index<NVP<T>, Others...> {
// will be 0 if the rest are all NVP<T>, otherwise -1
static constexpr std::size_t i = get_split_index<Others...>::i;
};
template<>
struct get_split_index<> {
static constexpr std::size_t i = 0;
};
template<typename... ATypes, typename... BTypes, std::size_t... ATT_I, std::size_t... NVP_I>
void Process(const std::tuple<const ATT<ATypes>&...>& att, const std::tuple<const NVP<BTypes>&...>& nvp, std::index_sequence<ATT_I...>, std::index_sequence<NVP_I...>) {
// Use (std::get<ATT_I>(att)) and (std::get<NVP_I>(nvp))
// instead of (atts) and (nvps) that you would use in your
// supposed `void Process(const ATT<ATypes>&..., const NVP<BTypes>&...)`
}
template<typename... Types, std::size_t... ATT_I, std::size_t... NVP_I>
void ProcessDispatch(const std::tuple<Types...>& t, std::index_sequence<ATT_I...> att_i, std::index_sequence<NVP_I...> nvp_i) {
detail::Process(std::forward_as_tuple(std::get<ATT_I>(t)...), std::forward_as_tuple(std::get<NVP_I + sizeof...(ATT_I)>(t)...), att_i, nvp_i);
}
}
template <typename ...Types>
void Process( const Types&... items ) {
constexpr std::size_t split_index = detail::get_split_index<Types...>::i;
if constexpr (split_index != -1) {
// Might want to check `&& sizeof...(Types) != 0`
detail::ProcessDispatch(std::forward_as_tuple(items...), std::make_index_sequence<split_index>{}, std::make_index_sequence<sizeof...(Types) - split_index>{});
} else {
// general case
}
}
template <typename T>
void Process( const T& t ) {
// single T
}
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps ) {
// one or more of type NVP<>
// This can also be folded into `detail::Process`, checking
// `if constexpr (sizeof...(BTypes) == 0)`.
}
Believe you can use a struct to help you here. The compiler can't determine where one parameter pack stops and the other begins, consider:
foo(1, 2.0, '3', "45", 6.0f). The first parameter pack could be nothing, the first, all of them or none of the above. There is no particular reason to prefer one over another. So you can't make a function that accepts two variadics. What you can do, is to split it into two structs, and specify explicitly the arguments for the outer class.
template<typename... Args>
struct S
{
template<typename... Inner>
static void Process(const ATT<Args>&... atts, const NVP<Inner>&... nvps) {}
};
Example for usage:
ATT<double> a1;
ATT<long> a2;
NVP<int> n1;
NVP<const char*> n2;
S<double, long>::Process(a1, a2, n1, n2);
Another version could be by using the constructor. Here, you also get auto-deduction which is easier. Unfortunately, it only works from C++17 and above.
template<typename... Args>
struct S
{
std::tuple<ATT<Args>...> tup;
S(const ATT<Args>&... atts)
: tup(atts...)
{}
template<typename... Inner>
void Process(const NVP<Inner>&... nvps){}
};
template<typename... Args>
S(const ATT<Args>&... atts)->S<Args...>;
And the usage is:
S(ATT(1), ATT(3.4)).Process(NVP("asdf"), NVP(3.4), NVP('f'));
return 0;
Assuming you're OK with getting them as tuples I made this after drawing from https://stackoverflow.com/a/12782697/1480324 :
#include <iostream>
#include <tuple>
template<typename T>
struct ATT {};
template<typename T>
struct NVP {};
template<typename... ATTs, typename... NVPs>
void Process(const std::tuple<ATT<ATTs>...>& atts, const std::tuple<NVP<NVPs>...>& nvps) {
std::cout << sizeof...(ATTs) << std::endl;
std::cout << sizeof...(NVPs) << std::endl;
}
int main() {
Process(std::make_tuple(ATT<int>(), ATT<double>()), std::make_tuple(NVP<std::string>(), NVP<bool>()));
return 0;
}
It compiles on https://www.onlinegdb.com/online_c++_compiler , but I can't test in visual studio.
Problem description:
C++17 introduces std::invocable<F, Args...>, which is nice to detect if a type... is invocable with the given arguments. However, would there be a way to do it for any arguments for functors (because combinations of the existing traits of the standard library already allow to detect functions, function pointers, function references, member functions...)?
In other words, how to implement the following type trait?
template <class F>
struct is_functor {
static constexpr bool value = /*using F::operator() in derived class works*/;
};
Example of use:
#include <iostream>
#include <type_traits>
struct class0 {
void f();
void g();
};
struct class1 {
void f();
void g();
void operator()(int);
};
struct class2 {
void operator()(int);
void operator()(double);
void operator()(double, double) const noexcept;
};
struct class3 {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
union union0 {
unsigned int x;
unsigned long long int y;
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
struct final_class final {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
int main(int argc, char* argv[]) {
std::cout << is_functor<int>::value;
std::cout << is_functor<class0>::value;
std::cout << is_functor<class1>::value;
std::cout << is_functor<class2>::value;
std::cout << is_functor<class3>::value;
std::cout << is_functor<union0>::value;
std::cout << is_functor<final_class>::value << std::endl;
return 0;
}
should output 001111X. In an ideal world, X should be 1, but I don't think it's doable in C++17 (see bonus section).
Edit:
This post seems to present a strategy that solves the problem. However, would there be a better/more elegant way to do it in C++17?
Bonus:
And as a bonus, would there be a way to make it work on final types (but that's completely optional and probably not doable)?
Building on my answer to my answer to this qustion, i was able to solve your problem, including the bonus one :-)
The following is the code posted in the other thread plus some little tweaks to get a special value when an object can't be called. The code needs c++17, so currently no MSVC...
#include<utility>
constexpr size_t max_arity = 10;
struct variadic_t
{
};
struct not_callable_t
{
};
namespace detail
{
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hece, to 'simulate' an arbitrary function signature.
template <size_t>
struct arbitrary_t
{
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&& ();
template <typename T>
operator T& ();
};
template <typename F, size_t... Is,
typename U = decltype(std::declval<F>()(arbitrary_t<Is>{}...))>
constexpr auto test_signature(std::index_sequence<Is...>)
{
return std::integral_constant<size_t, sizeof...(Is)>{};
}
template <size_t I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{}))
{
return {};
}
template <size_t I, typename F, std::enable_if_t<(I == 0), int> = 0>
constexpr auto arity_impl(...) {
return not_callable_t{};
}
template <size_t I, typename F, std::enable_if_t<(I > 0), int> = 0>
constexpr auto arity_impl(...)
{
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, size_t MaxArity = 10>
constexpr auto arity_impl()
{
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity + 1, F>(0);
if constexpr(std::is_same_v<std::decay_t<decltype(tmp)>, not_callable_t>) {
return not_callable_t{};
}
else if constexpr (tmp == MaxArity + 1)
{
// if that works, F is considered variadic
return variadic_t{};
}
else
{
// if not, tmp will be the correct arity of F
return tmp;
}
}
}
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity(F&& f) { return detail::arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity_v = detail::arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, size_t MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_t>;
// HERE'S THE IS_FUNCTOR
template<typename T>
constexpr bool is_functor_v = !std::is_same_v<std::decay_t<decltype(arity_v<T>)>, not_callable_t>;
Given the classes in yout question, the following compiles sucessfully (you can even use variadic lambdas:
constexpr auto lambda_func = [](auto...){};
void test_is_functor() {
static_assert(!is_functor_v<int>);
static_assert(!is_functor_v<class0>);
static_assert(is_functor_v<class1>);
static_assert(is_functor_v<class2>);
static_assert(is_functor_v<class3>);
static_assert(is_functor_v<union0>);
static_assert(is_functor_v<final_class>);
static_assert(is_functor_v<decltype(lambda_func)>);
}
See also a running example here.
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.
I am going to use MySQL connector. They provide functions to access the result row. Some examples are getString(1), getInt(1), getDate(2). The number inside the parenthesis is about the index of the result.
So that I have to use the following code to access this example row: 'John', 'M', 34
string name = row.getString(1);
string sex = row.getString(2);
int age = row.getInt(3);
I would like to try generic programming for various reasons (mainly for fun). But it was quite disappointing that I can't make it happens even with much time used.
The final result that I want:
std::tie<name, sex, age> = row.getResult<string, string, int>();
This functions should call the corresponding MySQL API.
It is also good to see any answer similar to below, although the syntax is wrong.
std::tie<name, sex, age> = row.getResult([string, string, int]);
Please don't suggest using for-loop. Let's try something more generic & functional ;-)
This works for me:
struct Row
{
template <int N, typename ... Args> struct Helper;
template <typename Arg1> struct Helper<1, Arg1>
{
static std::tuple<Arg1> getResult(Row& r)
{
return std::make_tuple(r.getResult<Arg1>(0));
}
};
template <int N, typename Arg1, typename ... Args>
struct Helper<N, Arg1, Args...>
{
static std::tuple <Arg1, Args ...> getResult(Row& r)
{
return std::tuple_cat(std::make_tuple(r.getResult<Arg1>(N-1)),
Helper<N-1, Args...>::getResult(r));
}
};
template <typename Arg> Arg getResult(int index)
{
// This is where the value needs to be extracted from the row.
// It is a dummy implementation for testing purposes.
return Arg{};
}
template <typename ... Args>
std::tuple <Args ...> getResult()
{
return Helper<sizeof...(Args), Args...>::getResult(*this);
}
};
Example usage:
Row r;
auto res1 = r.getResult<std::string>();
auto res2 = r.getResult<int>();
auto res3 = r.getResult<int, double, int>();
auto res4 = r.getResult<int, int, double, double>();
auto res5 = r.getResult<std::string, std::string, int, int, double, double>();
Working code: http://ideone.com/6IpJ8q
First you need to write these:
template<class T> T get( MySQLRow const& row, unsigned index);
template<>
int get<int>( MySQLRow const& row, unsigned index) { return connector.GetInt(index); }
// etc
Then, add some template meta programming tools:
template<class...>struct types{using type=types;};
the above could probably be replaced with std::tuple<?>* instead of types<?>. Maybe. But the above is clearer anyhow.
Next, this can be replaced with C++14's integral_sequence:
template<unsigned...>struct indexes{using type=indexes;};
template<unsigned max, unsigned...is>struct make_indexes<max-1, max-1, is...>{};
template<unsigned...is>struct make_indexes<0,is...>:indexes<is...>{};
template<unsigned max>using make_indexes_t=typename make_indexes<max>::type;
Finally, stuff:
namespace details {
template<unsigned... Is, class... Types>
std::tuple< Types... >
getResult( indexes<Is...>, types<Types...>, MySQLRow const& row ) {
return { get<Types>( row, Is+1 )... };
}
}
template<class... Types>
std::tuple<Types...>
getResult( MySQLRow const& row ) {
return details::getResult( make_indexes_t<sizeof...(Ts)>{}, types<Types...>{}, row );
}
syntax is:
getResult<int, double, std::string>( row );
assuming you write the various get functions and fix the type of MySQLRow to whatever it really is, and assuming the 1st row is 1.
Create the set of function Get overloads which implement unified interface to GetX methods of row:
#define DEFINE_GET_FOR_TYPE(Type, TypeName) \
void Get(Type& arg, const MySQLRow& row, std::size_t index) \
{ \
arg = row.Get##TypeName(index); \
}
DEFINE_GET_FOR_TYPE(int, Int)
DEFINE_GET_FOR_TYPE(std::string, String)
// ...
Here the macro DEFINE_GET_FOR_TYPE is used to create the necessary set.
Implement GetResult function template using C++14 std::index_sequence and std::make_index_sequence (they can be implemented in C++11 program, too):
struct Iterate
{
Iterate(...) {}
};
template <std::size_t... indices, typename... Types>
void GetResultImpl(const MySQLRow& row, std::index_sequence<indices...>, Types&... args)
{
Iterate{(Get(args, row, indices + 1), 0)...};
}
template <typename... Types>
void GetResult(const MySQLRow& row, Types&... args)
{
GetResultImpl(row, std::make_index_sequence<sizeof...(Types)>(), args...);
}
Use GetResult function template to get values from the row:
std::string name;
std::string sex;
int age;
GetResult(row, name, sex, age);
Live demo
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.