I'm am trying to pass a lambda (that captures local context by reference) to a function and ensure it is inlined.
I think a solution would be to use a function template and a C++20 concept, but I don't know how to write the concept of a lambda that takes specific types.
The aim is to factorize a complex loops on raster files, and let the caller execute code for each pixel.
The lambda call must be inlined.
I had bad luck with:
std::function: the performance with lambda is 2x worse than writing the whole loop in the caller code.
function pointer: lambda with capture (this or local variable) cannot be casted to function pointer
template method: I cannot overload it, and cannot check the lambda type as easily as with previous solutions.
Working code with bad performance:
// loop
template<typename T>
class Raster<T>
{
template<typename FileDataType>
void SaveToDisk(std::ostream& file, const std::function<FileDataType(T&)>& callback) const;
template<typename FileDataType>
void SaveToDisk(std::ostream& file, AnotherCallbackType...);
}
// call
class GeoRaster<double> : public Raster
{
void Test()
{
// Creates a file containing "short" values, from a GeoRaster<double>
SaveToDisk<short>(file, [&](double val)
{
// this lambda call must be inlined
if (val == this->nodata)
{
return 255;
}
else
{
return short(val) / 16;
}
}
}
});
Could you help me to write a concept like this:
template<typename T, typename FileDataType>
concept SimpleWriteCallback = requires(T a) {
{ a } -> std::convertible_to<std::function<FileDataType(T&)>>;
};
template<typename FileDataType, SimpleWriteCallback<FileDataType> Callback>
void SaveToDisk(std::ostream& file, Callback& callback) const;
I think that you are looking for is std::invocable, which is a concept that allows you to enforce that a type can be called with a given set of arguments:
#include <concepts>
#include <cstdio>
void do_stuff(std::invocable<int, int> auto &&fn) {
fn(3, 4);
}
int main() {
do_stuff([](const int a, const int b) { std::printf("%d %d\n", a, b); });
// do_stuff([] { std::puts("hi!"); }); // won't compile
return 0;
}
If you need to also constrain the return type of Func, you may use a requires() block to add a compound requirement:
template <typename Func>
requires requires(Func &&fn, std::vector<int> v) {
std::invocable<Func, const std::vector<int>&, bool>;
{ fn(v, false) } -> std::convertible_to<int>;
}
int do_stuff(std::vector<int> v, Func &&fn) {
v.push_back(31);
return fn(v, true);
}
int main() {
do_stuff({1, 2 ,3}, [](const auto &, bool) { return 3; }); // ok: right parameters, and return type is convertible to int
do_stuff({1, 2 ,3}, [](const auto &, bool) { return 3.f; }); // ok: float is also implicitly convertible to int
//do_stuff({1, 2 ,3}, [](const auto &v) { return 3; }); // won't compile, cannot be invoked with (const vector<int>&, bool)
//do_stuff({1, 2 ,3}, [](const auto &v, bool) { return ""; }); // won't compile, const char (&)[] is not convertible to int
}
First, we have to start with how we're even thinking about the constraint:
template<typename T, typename FileDataType>
concept SimpleWriteCallback = /* ... */;
We're not constraining the callback on a FileDataType, FileDataType is what the callback returns. Rather, we're constraining it on the type that we're passing into it:
template <typename F, typename T>
concept SimpleWriteCallback = std::invocable<F&, T&>;
And you'd then use this concept like:
template <SimpleWriteCallback<T> F>
void SaveToDisk(std::ostream& file, F&& callback) const {
using FileDataType = std::invoke_result_t<F&, T&>;
// ...
}
Now, separately, if you want to override the actual return type (as you seem to do, since the lambda returns an int but you want a short), you can provide an overload that lets you provide the type explicitly and have the other overload just forward to it:
template <typename FileDataType, SimpleWriteCallback<T> F>
requires std::convertible_to<std::invoke_result_t<F&, T&>, FileDataType>
void SaveToDisk(std::ostream& file, F&& callback) const {
// ...
}
template <SimpleWriteCallback<T> F>
void SaveToDisk(std::ostream& file, F&& callback) const {
// by default, FileDataType is the actual invocation result
SaveToDisk<std::invoke_result_t<F&, T&>>(file, callback);
}
For sorting user defined typed objects in a flexible way (i.e. by naming a member variable) I wrote a template to generate lambdas to do the comparison. Additionally to chain comparisons of different member variables in case of equality I wrote a second template. It works so far but I want bpth templates to be completely independent from any concrete types. Therefore I have to get a class type from a class member pointer type.
This is my user defined example type:
struct Person { string name; int age, height; };
To sort objects of it by looking at e.g. the age I want to write it like:
auto result = max_element(persons.begin(), persons.end(), order_by(&Person::age));
This works with the template:
template<class F> //F is Person::* e.g. &Person::age
auto order_by(F f) {
return [f](const Person& smaller, const Person& bigger) {
return smaller.*f < bigger.*f;
};
}
To be able to chain multiple comparisons in case of equal values like this:
result = max_element(persons.begin(), persons.end(), order_by(&Person::age) | order_by(&Person::height));
I wrote the template:
//compose two orderings :
template<class F1, class F2>
auto operator|(F1 f1, F2 f2) {
return [f1, f2](auto a, auto b) {
auto res = f1(a, b);
auto inv_res = f1(b, a);
if (res != inv_res)
return res;
return f2(a, b);
};
}
Here the first comparison is done and if it detects that a==b (a is not smaller than b and b is not smaller than a) it uses the second comparison function.
What I want to achieve is to be independent of the Person type in the first template. How could this be solved?
You can easily extract the class and type of the pointer-to-member in your first template with some small modifications.
template<class Class, class Type>
auto order_by(Type Class::* f) {
return [f](const Class& smaller, const Class& bigger) {
return smaller.*f < bigger.*f;
};
}
I would forward job to std::tuple with something like:
template <typename... Projs>
auto order_by(Projs... projs) {
return [=](const auto& lhs, const auto& rhs) {
return std::forward_as_tuple(std::invoke(projs, lhs)...)
< std::forward_as_tuple(std::invoke(projs, rhs)...);
};
}
with usage
result = std::max_element(persons.begin(), persons.end(), order_by(&Person::age, &Person::height));
ranges algorithms (C++20 or range-v3) separate comparison from projection, so you might have (by changing order_by to project_to):
result = ranges::max_element(persons, std::less<>{}, project_to(&Person::age, &Person::height));
You can get the class type from the type of a pointer to member like this:
#include <type_traits>
#include <iostream>
struct Foo {
int bar;
};
template <typename T>
struct type_from_member;
template <typename M,typename T>
struct type_from_member< M T::* > {
using type = T;
};
int main()
{
std::cout << std::is_same< type_from_member<decltype(&Foo::bar)>::type, Foo>::value;
}
Output:
1
Because type_from_member< decltype(&Foo::bar)>::type is Foo.
So you could use it like this:
template<class F> //F is Person::* e.g. &Person::age
auto order_by(F f) {
using T = typename type_from_member<F>::type;
return [f](const T& smaller, const T& bigger) {
return smaller.*f < bigger.*f;
};
}
I want to use std::any in my program but I find myself writing a lot of conditional statements like this:
if (anything.type() == typeid(short)) {
auto s = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
auto i = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
auto l = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
auto d = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
auto b = std::any_cast<bool>(anything);
}
Note that I omitted much of the else if conditions for brevity.
My program can use any of the defined types that can be stored in std::any so these if-then statements are quite long. Is there a way to refactor the code so that I can write it once?
My original inclination was to use templates like so:
template<typename T>
T AnyCastFunction(std::any) {
T type;
if (anything.type() == typeid(short)) {
type = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
type = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
type = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
type = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
type = std::any_cast<bool>(anything);
}
return type;
}
However, this leads to "couldn't deduce template parameter T" errors. How can I refactor this to avoid writing the large if/else blocks many times throughout the program?
If you have a known, fixed list of possible types, don't use std::any. Use std::variant<Ts...>. That makes Dietmar's answer look like this:
#include <variant>
void test(std::variant<int, double, char const*> v)
{
std::visit([](auto value){ std::cout << "value=" << value << "\n"; }, v);
}
which is the same thing, except (a) you don't have to implement visit yourself (b) this is massively more efficient at runtime and (c) this is type safe - you can't forget to check a particular type! Really even if you don't care about (a) or (b), (c) is a huge win.
And if you don't have a known, fixed list of possible types - which is the typical use-case for wanting std::any - then anything you're doing with std::any doesn't make sense anyway. You can't enumerate all possible copyable types (there are an infinite amount of them), so you can't necessarily retrieve the contents. So I really think variant is what you want.
Well, if you're sure you need such a broad range stored in any...
template<typename T> void visit(T &&t) { std::cout << "Hi " << t << "!\n"; }
void try_visit(std::any &&) { std::cout << "Unknown type\n"; }
template<typename T, typename... Ts> void try_visit(std::any thing) {
if(thing.type() == typeid(T)) {
visit(std::any_cast<T>(thing));
return;
}
if constexpr(sizeof...(Ts) > 0) try_visit<Ts...>(std::move(thing));
else try_visit(std::move(thing));
}
int main() {
try_visit<short, int, double, bool, long>(std::any{42});
}
%-}
I find this type of code fun to write.
any_visitor<types...> is a function object that visits a set of types.
You invoke it with an any followed by a function object. It then invokes the function object with whichever of the types... is in the any.
So you do any_vistor<int, double>{}( something, [](auto&& x) { /* some code */ } ).
If none of the types... are in the any, it invokes the function object with a std::any for you to deal with the extra case.
We can also write a variant that instead of passing the std::any to the functor, throws or returns false or something.
template<class...Ts>
struct any_visitor;
template<>
struct any_visitor<> {
template<class F>
decltype(auto) operator()( std::any& a, F&& f ) const {
return std::forward<F>(f)(a);
}
};
template<class...Ts>
struct any_visitor {
private:
struct accum {
std::size_t x = 0;
friend accum operator+( accum lhs, accum rhs ) {
if (lhs.x || rhs.x) return {lhs.x+1};
else return {};
}
};
public:
template<class Any, class F>
void operator()(Any&& any, F&& f) const {
// sizeof...(Ts) none in the list
// otherwise, index of which any is in the list
std::size_t which = sizeof...(Ts) - (accum{} + ... + accum{ any.type() == typeid(Ts) }).x;
using table_entry = void(*)(Any&&, F&&);
static const table_entry table[] = {
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::any_cast<Ts>( std::forward<Any>(any) ) );
}...,
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::forward<Any>(any) );
}
};
table[which]( std::forward<Any>(any), std::forward<F>(f) );
}
};
template<class...Fs>
struct overloaded:Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs&&...)->overloaded<std::decay_t<Fs>...>;
I also included overloaded which makes it easier to dispatch. If you want to handle all types uniformly, except handle an error case, you can do:
overloaded{
[](auto const& x){ std::cout << x << "\n"; },
[](std::any const&){ std::cout << "Unknown type\n"; }
}
and pass that as the function object to any_visitor.
Here is some test code:
std::any foo=7;
std::any bar=3.14;
auto visitor = overloaded{
[](int x){std::cout << x << "\n";},
[](auto&&){std::cout << "Unknown\n";}
};
any_visitor<int>{}( foo, visitor );
any_visitor<int>{}( bar, visitor );
which outputs:
7
Unknown
Live example.
Implementation wise, this code uses a dispatch table (sort of like a vtable) to map the index of the type stored in the any to which overload of the function object we invoke.
Yet another approach would be to write:
template<class...Ts>
std::optional<std::variant<Ts...>> to_variant( std::any );
which converts a std::any to a variant if its types match. Then use the usual visiting machinery on std::variant instead of rolling your own.
The basic idea is to create an std::any visitor and do the necessary processing in a function called from the visitor. That basic principle is straight forward. Let's start with supporting just one type:
#include <any>
#include <iostream>
#include <type_traits>
template <typename T, typename Any, typename Visitor>
auto any_visit1(Any&& any, Visitor visit)
-> std::enable_if_t<std::is_same_v<std::any, std::decay_t<Any>>>
{
if (any.type() == typeid(T)) {
visit(std::any_cast<T>(std::forward<Any>(any)));
}
}
int main() {
std::any a0(17);
any_visit1<int>(a0, [](auto value){ std::cout << "value=" << value << "\n"; });
}
The next step is to remove the one type restriction. As the explicit template parameters come first and are an open-ended list and the function object should be a deduced template parameter, you can't quite use a function template. However, a variable template (with an inline constexpr, of course, hence variable...) does the trick:
#include <any>
#include <iostream>
#include <type_traits>
template <typename... T>
inline constexpr auto any_visit =
[](auto&& any, auto visit) -> std::enable_if_t<std::is_same_v<std::any, std::decay_t<decltype(any)>>> {
(
(any.type() == typeid(T) && (visit(std::any_cast<T>(std::forward<decltype(any)>(any))), true))
|| ...)
// Uncomment the line below to have visit(any) called for unhandled types
// || (visit(std::forward<decltype(any)>(any)), true)
;
};
void test(std::any any)
{
any_visit<int, double, char const*>(any, [](auto value){ std::cout << "value=" << value << "\n"; });
}
int main() {
test(17);
test(3.14);
test(+"foo");
}
If you need multiple std::any objects decoded you'd just pass suitable [lambda?] functions into it which refer to the other objects and keep building up the object until you got all the ones you need.
Here is code that I hope explains what I want to achieve.
vector<int> ints;
vector<double> doubles;
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args) {
// TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
// - Call "f(args)"
}
// Example:
void copy(int a, double& b) {
b = a;
}
int test() {
Call(copy, {{Int, 3}, {Double, 2}}); // copy(ints[3], double[2]);
}
Can this be done in C++11 ?
If yes, can the solution be simplified in C++14 ?
I'd do this in two steps.
First, I'd wrap f in an object able to understand Arg-like parameters, and generate errors on failure. For simplicity, suppose we throw.
This is a bit simpler than your Arg to be understood at this layer, so I might translate Arg into MyArg:
struct MyArg {
MyArg(MyArg const&)=default;
MyArg(int* p):i(p){}
MyArg(double* p):d(p){}
MyArg(Arg a):MyArg(
(a.type==Arg::Int)?
MyArg(&ints.at(a.index)):
MyArg(&doubles.at(a.index))
) {}
int * i = nullptr;
double* d = nullptr;
operator int&(){ if (!i) throw std::invalid_argument(""); return *i; }
operator double&(){ if (!d) throw std::invalid_argument(""); return *d; }
};
We map void(*)(Ts...) to std::function<void(MyArg, MyArg, MyArg)> like this:
template<class T0, class T1>using second_type = T1;
template<class...Ts>
std::function<void( second_type<Ts,MyArg>... )> // auto in C++14
my_wrap( void(*f)(Ts...) ) {
return [f](second_type<Ts,MyArg>...args){
f(args...);
};
}
now all that is left is counting function parameter count vs vector size count, and unpacking the std::vector into a function call.
The last looks like:
template<class...Ts, size_t...Is>
void call( std::function<void(Ts...)> f, std::index_sequence<Is...>, std::vector<Arg> const& v ) {
f( v[Is]... );
}
template<class...Ts>
void call( std::function<void(Ts...)> f, std::vector<Arg> const& v ) {
call( std::move(f), std::index_sequence_for<Ts...>{}, v );
}
where index_sequence and index_sequence_for are C++14, but equivalents can be implemented in C++11 (there are many implementations on stack overflow).
So we end up with something like:
template<class...Ts>
void Call( void(*pf)(Ts...), std::vector<Arg> const& v ) {
if (sizeof...(Ts)>v.size())
throw std::invalid_argument("");
auto f = my_wrap(pf);
call( std::move(f), v );
}
Dealing with the throws is left as an exercise, as is handling return values.
This code has not been compiled or tested, but the design should be sound. It only supports calling function pointers -- calling generalized callable objects is tricky, because counting how many arguments they want (of type int or double) is tricky. If you passed in how many arguments they want as a compile-time constant, it is easy. You could also build a magic switch that handles counts up to some constant (10, 20, 1000, whatever), and dispatch the runtime length of the vector into a compile time constant that throws on a argument length mismatch.
This is trickier.
The hard coded pointers sort of suck.
template<class...Ts>struct types{using type=types;};
template<size_t I> using index=std::integral_constant<size_t, I>;
template<class T, class types> struct index_in;
template<class T, class...Ts>
struct index_in<T, types<T,Ts...>>:
index<0>
{};
template<class T, class T0, class...Ts>
struct index_in<T, types<T0,Ts...>>:
index<1+index_in<T, types<Ts...>>{}>
{};
is a package of types.
Here is how we can store buffers:
template<class types>
struct buffers;
template<class...Ts>
struct buffers<types<Ts...>> {
struct raw_view {
void* start = 0;
size_t length = 0;
};
template<class T>
struct view {
T* start = 0;
T* finish = 0;
view(T* s, T* f):start(s), finish(f) {}
size_t size() const { return finish-start; }
T& operator[](size_t i)const{
if (i > size()) throw std::invalid_argument("");
return start[i];
}
}
std::array< raw_view, sizeof...(Ts) > views;
template<size_t I>
using T = std::tuple_element_t< std::tuple<Ts...>, I >;
template<class T>
using I = index_of<T, types<Ts...> >;
template<size_t I>
view<T<I>> get_view() const {
raw_view raw = views[I];
if (raw.length==0) { return {0,0}; }
return { static_cast<T<I>*>(raw.start), raw.length/sizeof(T) };
}
template<class T>
view<T> get_view() const {
return get_view< I<T>{} >();
}
template<class T>
void set_view( view<T> v ) {
raw_view raw{ v.start, v.finish-v.start };
buffers[ I<T>{} ] = raw;
}
};
now we modify Call:
template<class R, class...Args, size_t...Is, class types>
R internal_call( R(*f)(Args...), std::vector<size_t> const& indexes, buffers<types> const& views, std::index_sequence<Is...> ) {
if (sizeof...(Args) != indexes.size()) throw std::invalid_argument("");
return f( views.get_view<Args>()[indexes[Is]]... );
}
template<class R, class...Args, size_t...Is, class types>
R Call( R(*f)(Args...), std::vector<size_t> const& indexes, buffers<types> const& views ) {
return internal_call( f, indexes, views, std::index_sequence_for<Args...>{} );
}
which is C++14, but most components can be translated to C++11.
This uses O(1) array lookups, no maps. You are responsible for populating buffers<types> with the buffers, sort of like this:
buffers<types<double, int>> bufs;
std::vector<double> d = {1.0, 3.14};
std::vector<int> i = {1,2,3};
bufs.set_view<int>( { i.data(), i.data()+i.size() } );
bufs.set_view<double>( { d.data(), d.data()+d.size() } );
parameter mismatch counts and index out of range generate thrown errors. It only works with raw function pointers -- making it work with anything with a fixed (non-template) signature is easy (like a std::function).
Making it work with an object with no signature is harder. Basically instead of relying on the function called for the arguments, you instead build the cross product of the types<Ts...> up to some fixed size. You build a (large) table of which of these are valid calls to the passed in call target (at compile time), then at run time walk that table and determine if the arguments passed in are valid to call the object with.
It gets messy.
This is why my above version simply asks for indexes, and deduces the types from the object being called.
I have a partial solution, using C++11 grammar.
First I make a function overloader accepting arbitrator kinds of arguments
template< typename Function >
struct overloader : Function
{
overloader( Function const& func ) : Function{ func } {}
void operator()(...) const {}
};
template< typename Function >
overloader<Function> make_overloader( Function const& func )
{
return overloader<Function>{ func };
}
then, using the overloader to deceive the compiler into believing the following code ( in switch-case block )is legal:
template <typename F>
void Call( F const& f, const vector<Arg>& args )
{
struct converter
{
Arg const& arg;
operator double&() const
{
assert( arg.type == Double );
return doubles[arg.index];
}
operator int() const
{
assert( arg.type == Int );
return ints[arg.index];
}
converter( Arg const& arg_ ): arg( arg_ ) {}
};
auto function_overloader = make_overloader( f );
unsigned long const arg_length = args.size();
switch (arg_length)
{
case 0 :
function_overloader();
break;
case 1 :
function_overloader( converter{args[0]} );
break;
case 2 :
function_overloader( converter{args[0]}, converter{args[1]} );
break;
case 3 :
function_overloader( converter{args[0]}, converter{args[1]}, converter{args[2]} );
break;
/*
case 4 :
.
.
.
case 127 :
*/
}
}
and test it this way:
void test_1()
{
Call( []( int a, double& b ){ b = a; }, vector<Arg>{ Arg{Int, 3}, Arg{Double, 2} } );
}
void test_2()
{
Call( []( double& b ){ b = 3.14; }, vector<Arg>{ Arg{Double, 0} } );
}
void my_copy( int a, double& b, double& c )
{
b = a;
c = a+a;
}
void test_3()
{
//Call( my_copy, vector<Arg>{ Arg{Int, 4}, Arg{Double, 3}, Arg{Double, 1} } ); // -- this one does not work
Call( []( int a, double& b, double& c ){ my_copy(a, b, c); }, vector<Arg>{ Arg{Int, 4}, Arg{Double, 3}, Arg{Double, 1} } );
}
the problems with this solution is:
g++5.2 accept it, clang++6.1 doesn's
when the argument(s) of function Call is/are not legal, it remains silent
the first argument of function Call cannot be a C-style function, one must wrap that into a lambda object to make it work.
the code is available here - http://melpon.org/wandbox/permlink/CHZxVfLM92h1LACf -- for you to play with.
First of all, you need some mechanism to register your argument values that are later referenced by some type and an index:
class argument_registry
{
public:
// register a range of arguments of type T
template <class T, class Iterator>
void register_range(Iterator begin, Iterator end)
{
// enclose the range in a argument_range object and put it in our map
m_registry.emplace(typeid(T), std::make_unique<argument_range<T, Iterator>>(begin, end));
}
template <class T>
const T& get_argument(size_t idx) const
{
// check if we have a registered range for this type
auto itr = m_registry.find(typeid(T));
if (itr == m_registry.end())
{
throw std::invalid_argument("no arguments registered for this type");
}
// we are certain about the type, so downcast the argument_range object and query the argument
auto range = static_cast<const argument_range_base1<T>*>(itr->second.get());
return range->get(idx);
}
private:
// base class so we can delete the range objects properly
struct argument_range_base0
{
virtual ~argument_range_base0(){};
};
// interface for querying arguments
template <class T>
struct argument_range_base1 : argument_range_base0
{
virtual const T& get(size_t idx) const = 0;
};
// implements get by querying a registered range of arguments
template <class T, class Iterator>
struct argument_range : argument_range_base1<T>
{
argument_range(Iterator begin, Iterator end)
: m_begin{ begin }, m_count{ size_t(std::distance(begin, end)) } {}
const T& get(size_t idx) const override
{
if (idx >= m_count)
throw std::invalid_argument("argument index out of bounds");
auto it = m_begin;
std::advance(it, idx);
return *it;
}
Iterator m_begin;
size_t m_count;
};
std::map<std::type_index, std::unique_ptr<argument_range_base0>> m_registry;
};
Than we define a small type to combine a type and a numerical index for referencing arguments:
typedef std::pair<std::type_index, size_t> argument_index;
// helper function for creating an argument_index
template <class T>
argument_index arg(size_t idx)
{
return{ typeid(T), idx };
}
Finally, we need some template recursion to go through all expected arguments of a function, check if the user passed an argument of matching type and query it from the registry:
// helper trait for call function; called when there are unhandled arguments left
template <bool Done>
struct call_helper
{
template <class FuncRet, class ArgTuple, size_t N, class F, class... ExpandedArgs>
static FuncRet call(F func, const argument_registry& registry, const std::vector<argument_index>& args, ExpandedArgs&&... expanded_args)
{
// check if there are any arguments left in the passed vector
if (N == args.size())
{
throw std::invalid_argument("not enough arguments");
}
// get the type of the Nth argument
typedef typename std::tuple_element<N, ArgTuple>::type arg_type;
// check if the type matches the argument_index from our vector
if (std::type_index{ typeid(arg_type) } != args[N].first)
{
throw std::invalid_argument("argument of wrong type");
}
// query the argument from the registry
auto& arg = registry.get_argument<arg_type>(args[N].second);
// add the argument to the ExpandedArgs pack and continue the recursion with the next argument N + 1
return call_helper<std::tuple_size<ArgTuple>::value == N + 1>::template call<FuncRet, ArgTuple, N + 1>(func, registry, args, std::forward<ExpandedArgs>(expanded_args)..., arg);
}
};
// helper trait for call function; called when there are no arguments left
template <>
struct call_helper<true>
{
template <class FuncRet, class ArgTuple, size_t N, class F, class... ExpandedArgs>
static FuncRet call(F func, const argument_registry&, const std::vector<argument_index>& args, ExpandedArgs&&... expanded_args)
{
if (N != args.size())
{
// unexpected arguments in the vector
throw std::invalid_argument("too many arguments");
}
// call the function with all the expanded arguments
return func(std::forward<ExpandedArgs>(expanded_args)...);
}
};
// call function can only work on "real", plain functions
// as you could never do dynamic overload resolution in C++
template <class Ret, class... Args>
Ret call(Ret(*func)(Args...), const argument_registry& registry, const std::vector<argument_index>& args)
{
// put the argument types into a tuple for easier handling
typedef std::tuple<Args...> arg_tuple;
// start the call_helper recursion
return call_helper<sizeof...(Args) == 0>::template call<Ret, arg_tuple, 0>(func, registry, args);
}
Now you can use it like this:
int foo(int i, const double& d, const char* str)
{
printf("called foo with %d, %f, %s", i, d, str);
// return something
return 0;
}
int main()
{
// prepare some arguments
std::vector<int> ints = { 1, 2, 3 };
std::vector<double> doubles = { 10., 20., 30. };
std::vector<const char*> str = { "alpha", "bravo", "charlie" };
// register them
argument_registry registry;
registry.register_range<int>(ints.begin(), ints.end());
registry.register_range<double>(doubles.begin(), doubles.end());
registry.register_range<const char*>(str.begin(), str.end());
// call function foo with arguments from the registry
return call(foo, registry, {arg<int>(2), arg<double>(0), arg<const char*>(1)});
}
Live example: http://coliru.stacked-crooked.com/a/7350319f88d86c53
This design should be open for any argument type without the need to list all the supported types somewhere.
As noted in the code comments, you cannot call any callable object like this in general, because overload resolution could never be done at runtime in C++.
Instead of clarifying the question, as I requested, you have put it up for bounty. Except if that really is the question, i.e. a homework assignment with no use case, just exercising you on general basic programming, except for that only sheer luck will then give you an answer to your real question: people have to guess about what the problem to be solved, is. That's the reason why nobody's bothered, even with the bounty, to present a solution to the when-obvious-errors-are-corrected exceedingly trivial question that you literally pose, namely how to do exactly this:
vector<int> ints;
vector<double> doubles;
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args) {
// TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
// - Call "f(args)"
}
// Example:
void copy(int a, double& b) {
b = a;
}
int test() {
Call(copy, {{Int, 3}, {Double, 2}}); // copy(ints[3], double[2]);
}
In C++11 and later one very direct way is this:
#include <assert.h>
#include <vector>
using std::vector;
namespace g {
vector<int> ints;
vector<double> doubles;
}
struct Arg {
enum Type {
Int,
Double
};
Type type;
int index;
};
template <typename F>
void Call(const F& f, const vector<Arg>& args)
{
// Was TODO:
// - First assert that count and types or arguments of <f> agree with <args>.
assert( args.size() == 2 );
assert( args[0].type == Arg::Int );
assert( int( g::ints.size() ) > args[0].index );
assert( args[1].type == Arg::Double );
assert( int( g::doubles.size() ) > args[1].index );
// - Call "f(args)"
f( g::ints[args[0].index], g::doubles[args[1].index] );
}
// Example:
void copy(int a, double& b)
{
b = a;
}
auto test()
{
Call(copy, {{Arg::Int, 3}, {Arg::Double, 2}}); // copy(ints[3], double[2]);
}
namespace h {}
auto main()
-> int
{
g::ints = {000, 100, 200, 300};
g::doubles = {1.62, 2.72, 3.14};
test();
assert( g::doubles[2] == 300 );
}
There are no particularly relevant new features in C++14.
I propose this answer following my comment on your question. Seeing that in the requirements, you stated:
Preferably we should not be required to create a struct that
enumerates all the types we want to support.
It could suggests you would like to get rid of the type enumerator in your Arg structure. Then, only the value would be left: then why not using plain C++ types directly, instead of wrapping them ?
It assumes you then know all your argument types at compile time
(This assumption could be very wrong, but I did not see any requirement in your question preventing it. I would be glad to rewrite my answer if you give more details).
The C++11 variadic template solution
Now to the solution, using C++11 variadic templates and perfect forwarding. In a file Call.h:
template <class F, class... T_Args>
void Call(F f, T_Args &&... args)
{
f(std::forward<T_Args>(args)...);
}
Solution properties
This approach seems to satisfy all your explicit requirements:
Works with C++11 standard
Checks that count and types or arguments of f agress with args.
It actually does that early, at compile time, instead of a possible runtime approach.
No need to manually enumerate the accepted types (actually works with any C++ type, be it native or user defined)
Not in your requirement, but nice to have:
Very compact, because it leverage a native features introduced in C++11.
Accepts any number of arguments
The type of the argument and the type of the corresponding f parameter do not have to match exactly, but have to be compatible (exactly like a plain C++ function call).
Example usage
You could test it in a simple main.cpp file:
#include "Call.h"
#include <iostream>
void copy(int a, double& b)
{
b = a;
}
void main()
{
int a = 5;
double b = 6.2;
std::cout << "b before: " << b << std::endl;
Call(copy, a, b);
std::cout << "b now: " << b << std::endl;
}
Which would print:
b before: 6.2
b now: 5