variadic template arguments with default values - c++

I have a templated struct that has a method that (along with other parameters) accepts those templated parameters.
template<class... Types>
struct Observer
{
void subscribe(const std::string& id, Types... args)
{
// store id somehow
data = std::make_tuple(args...);
}
std::tuple<Types...> data;
};
I want to make all the templated arguments optional. So that it looks like this:
Observer<float, int> observer;
observer.subscribe("id1", 3.14, 4);
observer.subscribe("id2", 0.707); // this doesn't work!
observer.subscribe("id3"); // this doesn't work!
As far as I know there is not straight forward way? But maybe someone know a workaround or a trick.
Ideally, I'd like to provide my own defaults. Maybe like this:
enum class SomeEnum { Val1, Val2 };
Observer<float, SomeEnum, 0.f, SomeEnum::Val1> observer;
observer.subscribe("id1", 3.14);
Here is LIVE EXAMPLE.

In C++17 you can simply do something like:
template<class... Types>
struct Observer
{
static constexpr std::tuple<Types...> defaults{42, 24, 99};
template<class... Args>
void subscribe(Args... args)
{
if constexpr (sizeof...(Types) > sizeof...(Args)) {
subscribe(args..., std::get<sizeof...(Args)>(defaults));
} else {
// whatever you need with `args...`
}
}
};
Here I am simply picking them from Observer::defaults, but feel free to compute them however you want.
For C++14 and below, you will need to emulate the if constexpr. See e.g. Constexpr if alternative for alternatives.

Boost.Mp11 for the win:
template <typename... Ts>
void subscribe(const std::string& id, Ts const&... args)
{
static_assert(sizeof...(Ts) <= sizeof...(Types));
using Rest = mp_drop_c<std::tuple<Types...>, sizeof...(Ts)>;
data = std::tuple_cat(std::make_tuple(args...), Rest());
}
The assumes that value initialization of the trailing arguments is fine. If it's not, you'll have to figure out what to do with Us.
Works nicer if you actually make the optional more explicit:
template<class... Types>
struct Observer
{
using Data = std::tuple<std::optional<Types>...>;
template <typename... Ts>
void subscribe(const std::string& id, Ts const&... args)
{
static_assert(sizeof...(Ts) <= sizeof...(Types));
using Rest = mp_drop_c<Data, sizeof...(Ts)>;
data = std::tuple_cat(std::make_tuple(args...), Rest());
}
Data data;
};

Related

Passing std::vector of wrapper of std::variant into variadic class, wrapping variadic method without knowing the specifics

The goal
I try to create a set of classes that removes boilerplate code for implementing extensions to a game in C++.
For that, I have a designated value class, that can hold one of the following types:
float, std::string, bool, std::vector<value>, void
For that, I would like to have a host class to which I can add one or more method instances like follows:
using namespace std::string_literals;
host h;
h.add(
method<bool, req<std::string>, req<std::string>, opt<bool>>("compare_strings"s,
[](std::string s_orig, std::string s_comp, std::optional<bool> ingore_case) -> bool {
if (ignore_case.has_value() && ignore_case.value()) {
// ... lowercase both
}
return s_orig.compare(s_comp) == 0;
}));
Note that req<T> should be a meta info that a given value is required, opt<T> a meta info that a given value is not required and may only be provided after all required parameters.
The host class now contains a method execute(std::string function, std::vector<value> values) with function and values originating from a method getting char* for method and ´char** argv+ int argcfor values. Theexecutemethod now is supposed to call the correctmethod` instances function
value host::execute(std::string function, std::vector<value> values) {
// get matching method group
std::vector<method> mthds = m_methods[function];
// get matching parameter list
for (method& mthd : mthds) {
if (mthd.can_call(mthds, values)) {
// call generic method
auto res = mthd.call_generic(values);
// pass result back to callee
// return [...]
}
}
// return error back to callee
// return [...]
}
which means that the actual method class now needs to mangle two methods properly can_call and call_generic.
The value class has corresponding template<typename T> bool is() and template<typename T> T get() methods.
What remains
I did have other attempts at this, but as those failed, I deleted them (not very smart in hindside, but needed to get the whole thing out as another person relied on the results working) and now cannot figure out another attempt then prior ... so this is what I am left with as of now:
class method_base
{
public:
template<typename T> struct in { using type = T; };
template<typename T> struct opt { using type = T; };
public:
virtual bool can_call(std::vector<sqf::value> values) = 0;
virtual sqf::value call_generic(std::vector<sqf::value> values) = 0;
};
template<typename T, typename ... TArgs>
class method : public method_base
{
func m_func;
sqf::value val
public:
using func = T(*)(TArgs...);
method(func f) : m_func(f) {}
virtual retval can_call(std::vector<sqf::value> values) override
{
}
};
Appendix
If something is unclear, confusing or you just have further questions, please do ask them. I will try my best to rephrase whatever is unclear as this will help greatly with developing further extensions in the future, possibly defining a "go to" way for how to create extensions in the community for the game in question (Arma 3 just in case somebody wondered)
I may note that this is pretty much my first deep dive into meta programming so things I present may not be possible at all. If so, I kindly would like to ask you if you may also explain why that is so and the thing I attempt is not possible.
The Solution
I do want to express my thanks to all who answered this question again. I ended up combining pretty much parts of all solutions here and pretty much learned a lot on the way. The final implementation I ended up with looks like the following:
namespace meta
{
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
template <typename ArgType>
struct def_value { static ArgType value() { return {}; } };
template <typename ArgType>
struct get_type { using type = ArgType; };
template <typename ArgType>
struct get_type<std::optional<ArgType>> { using type = ArgType; };
}
struct method {
std::function<bool(const std::vector<value>&)> m_can_call;
std::function<value(const std::vector<value>&)> m_call;
template <typename ... Args, std::size_t... IndexSequence>
static bool can_call_impl(const std::vector<value>& values, std::index_sequence<IndexSequence...> s) {
// values max args
return values.size() <= sizeof...(Args) &&
// for every Arg, either...
(... && (
// the value provides that argument and its the correct type, or...
(IndexSequence < values.size() && sqf::is<sqf::meta::get_type<Args>::type>(values[IndexSequence])) ||
// the value does not provide that argument and the arg is an optional
(IndexSequence >= values.size() && sqf::meta::is_optional_v<Args>)
));
}
template <typename Ret, typename ... Args, std::size_t... IndexSequence>
static value call_impl(std::function<Ret(Args...)> f, const std::vector<value>& values, std::index_sequence<IndexSequence...>) {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(f,
(IndexSequence < values.size() ? sqf::get<sqf::meta::get_type<Args>::type>(values[IndexSequence])
: sqf::meta::def_value<Args>::value())...)
};
}
public:
template <typename Ret, typename ... Args>
method(std::function<Ret(Args...)> f) :
m_can_call([](const std::vector<value>& values) -> bool
{
return can_call_impl<Args...>(values, std::index_sequence_for<Args...>{});
}),
m_call([f](const std::vector<value>& values) -> value
{
return call_impl<Ret, Args...>(f, values, std::index_sequence_for<Args...>{});
})
{
}
bool can_call(const std::vector<value>& values) const { return m_can_call(values); }
value call_generic(const std::vector<value>& values) const { return m_call(values); }
// to handle lambda
template <typename F>
method static create(F f) { return method{ std::function{f} }; }
};
Assumming a way to check current type of value (template <typename T> bool value::isA<T>()) and a way to retrieve the value (template <typename T> /*const*/T& get(/*const*/ value&))
It seems you might do:
struct method
{
template <typename Ret, typename ... Ts>
method(std::function<Ret(Ts...)> f) : method(std::index_sequence<sizeof...(Ts)>(), f)
{}
template <typename Ret, typename ... Ts, std::size_t ... Is>
method(std::index_sequence<Is...>, std::function<Ret(Ts...)> f) :
isOk([](const std::vector<value>& values) {
return ((values.size() == sizeof...(Is)) && ... && values[Is].isA<Ts>());
}),
call([f](const std::vector<value>& values){
return f(get<Ts>(values[Is])...);
})
{}
// to handle lambda
template <typename F>
static fromCallable(F f) { return method{std::function{f}}; }
std::function<bool(const std::vector<value>&)> isOk;
std::function<value(const std::vector<value>&)> call;
};
Here's a quick example including the machinery for ret<T> and opt<T>. You haven't given any information on what value is, so I'm going to assume something like:
struct value {
// using `std::monostate` instead of `void`
std::variant<float, std::string, bool, std::vector<value>, std::monostate> data;
};
(I'm assuming c++17 for this answer.)
From there, we need our metatypes and a few traits to branch off them. I implement them using partial specialisations, but there are other ways too.
// types to determine optional vs. required
template <typename T>
struct req { using type = T; };
template <typename T>
struct opt { using type = T; };
// trait to determine if it's an optional type
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<opt<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
// get the "real" function parameter type
template <typename ArgType>
struct real_type;
template <typename ArgType>
using real_type_t = typename real_type<ArgType>::type;
template <typename T>
struct real_type<req<T>> { using type = T; };
template <typename T>
struct real_type<opt<T>> { using type = std::optional<T>; };
Now we implement method. I'll use a similar polymorphic relationship with method_base as you do in your partial demo; I also template on the function type passed in, to allow e.g. the functions to use const references to the type instead of the type itself.
The implementation itself uses the common trick of delegating to helper functions with std::index_sequence and fold expressions to "iterate" through the variadic template args.
// base class for polymorphism
struct method_base {
virtual ~method_base() = default;
virtual bool can_call(const std::vector<value>& values) const = 0;
virtual value call_generic(const std::vector<value>& values) const = 0;
};
// provide a different method implementation for each set of args
// I also overload on
template<typename RetType, typename Fn, typename... Args>
struct method : method_base {
private:
Fn func;
static_assert(std::is_invocable_r_v<RetType, Fn, real_type_t<Args>...>,
"function must be callable with given args");
public:
// accept any function that looks sort of like what we expect;
// static assert above makes sure it's sensible
template <typename G>
method(G&& func) : func(std::forward<G>(func)) {}
template <std::size_t... Is>
bool can_call_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
// for every Arg, either...
return (... and (
// the value provides that argument and its the correct type, or...
(Is < values.size() and std::holds_alternative<typename Args::type>(values[Is].data))
// the value does not provide that argument and the arg is an optional
or (Is >= values.size() and is_optional_v<Args>)
));
}
bool can_call(const std::vector<value>& values) const override {
return can_call_impl(values, std::index_sequence_for<Args...>{});
}
template <std::size_t... Is>
value call_generic_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(func,
(Is < values.size() ? std::get<typename Args::type>(values[Is].data)
: real_type_t<Args>{})...)
};
}
value call_generic(const std::vector<value>& values) const override {
return call_generic_impl(values, std::index_sequence_for<Args...>{});
}
};
I'll also create a helper function to make methods:
template <typename RetType, typename... Args, typename Fn>
std::unique_ptr<method_base> make_method(Fn&& func) {
return std::make_unique<method<RetType, std::decay_t<Fn>, Args...>>(std::forward<Fn>(func));
}
Live example.
It's not perfect, but this should give you a general idea of how to do it.
Change your method to:
method< R(Args...) >
your tags seem useless. Detect optional with ... std::optional.
For storage, use std variant. Use some non-void type for void (I don't care what).
As a first pass we aim for perfect compatibility.
template<class...Args>
struct check_signature {
bool operator()( std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
std::size_t i=0;
return (std::holds_alternative<Args>(values[i++])&&...);
}
};
this can be stored in a std::function<bool(std::span<value const>)> or just called in your class impementation.
Similar code can store the callable.
template<class F, class R, class...Args>
struct execute {
F f;
template<std::size_t...Is>
R operator()( std::index_sequence<Is...>, std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
return f( std::get<Args>(values[Is])... );
}
R operator()( std::span<value const> values ) const {
return (*this)( std::make_index_sequence<sizeof...(Args)>{}, values );
}
};
some work may have to be done for the fake void.
Your method is now a aggregate.
struct method {
std::function<bool(std::span<value const>)> can_call;
std::function<value(std::span<value const>)> execute;
};
if you want it to be. The two template objects above can be stored in these two std functions.
There are probably tpyos, I just wrote this on my phone and have not tested it.
Extending this to cover optional args is a little bit of work. But nothing hard.
In both cases, you'll write a helper function that says if the argument is compatible or generates the value based on if you are past the end of the incoming vector.
Ie, std::get<Args>(values[Is])... becomes getArgFrom<Is, Args>{}(values)..., and we specialize for std optional producing nullopt if Is>values.size().

How to store various types callback functions in one container

I need to store callback functions from opencl to later execution, e.g:
void pfn_notify(const char *errinfo, const void *private_info, size_t cb, void *user_data){
fprintf(stderr, "OpenCL Error (via pfn_notify): %s\n", errinfo);
}
These callback functions vary, so I would like to unify storing method and store pointers to them in some kind of container(vector, map etc.). In addition some of the arguments needs to be passed after storing instead of binding in the moment of pushing into container.
General pseudocode scheme:
...some code in main...
Client.storecallback(function_to_store(some_arguments));
...rest code in main...
class Client{
void storecallback(void*(*)(some_arguments) function){
callback_thread.store(function);
callback_thread.start();
}
CallbackThread callback_thread;
};
class CallbackThread() {
void start(){
/* make connection via tcp/ip */
receive();
}
void store(void*(*) function){
callback.store(key, function);
}
void receive() {
Buffer* input = new Buffer(tcp_socket);
int a = input->pull();
char b = input->pull();
callback.raise(key, a, b);
}
Callback callback;
};
class Callback {
std::map<uint64_t key, void*(*) function> callback_map;
void store(void*(*) function){
callback_map[key] = function;
}
template<typename... Args>
void raise(uint64_t key, Args... rest_of_arguments){
callback_map[key](rest_of_arguments);
}
};
I am aware that i need extra class for unification, some kind of Functor class.
With use of std::function and std::bind I am able to unificate stored variable type to std::function<void()> however, I can not change/bind new arguments to callback functions. This solution requries universal struct which will store pointers to arguments variables and replace/fill them with data before calling stored function and I have no idea how to create such an universal structure other than structure templates for each of callback function which is not really nice solution for me.
With assist of this Indicies, and this Stackoverflow I was able to create solution in which I can unificate creation process with placeholders which allows me to add some arguments in moment of calling, not only storing:
namespace project_name {
namespace detail {
template<int I> struct placeholder {};
}
}
namespace std {
template<int I>
struct is_placeholder< project_name::detail::placeholder<I> > :
std::integral_constant<int, I>{};
}
namespace project_name {
namespace detail {
template <size_t... Is>
struct indices {};
template <size_t N, std::size_t... Is>
struct build_indices : build_indices<N - 1, N - 1, Is...> {};
template <size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
template<std::size_t... Is, class F, class... Args>
inline auto easy_bind(indices<Is...>, F const& f, Args&&... args) ->
decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1> {}...)){
return std::bind(
f, std::forward<Args>(args)..., placeholder<Is + 1> {} ...);
}
}
class Functor {
public:
template<class R, class... FArgs, class... Args>
Functor(std::function<R(FArgs...)> f, Args&&... args) {}
template<class R, class... FArgs, class... Args>
static inline auto easy_bind(std::function<R(FArgs...)> f, Args&&... args) -> decltype(detail::easy_bind(
detail::build_indices<sizeof...(FArgs) - sizeof...(Args)> {},
f, std::forward<Args>(args)...)) {
return detail::easy_bind(
detail::build_indices<sizeof...(FArgs) - sizeof...(Args)> {}, f, std::forward<Args>(args)...);
}
}
Unfortunately now I have no clue how to unify storing this, as returned types are different when using std::placeholder.
Here are my questions then:
Which of these two approaches is better and how to solve problems of this approach.
Maybe there is other approach which I should consider but did not think of due to lack of knowledge.
EDIT:
Your comments with links to threads about problems not even close to mine are absolutely not useful. Read what question is about before linking something that I have already implemented and inserted into my question. Solved it anyway, I'll add solution for further generations soon.
Okay, kinda late but I wanted to be sure about it and also Christmas holidays occured. Here is full example of templated class for Functors which can be later stored in map and easily called with various arguments. This is what I was talking about and I'm sure no one provided any useful advice except Some programmer dude(maybe because of my poor explanation). We use some fancy tricks here(which were mostly provided by stack overflow and documentation).
Here is sketch of class which holds all of our callbacks, we store unified callback functions by refactoring them to std::shared_ptr<void>. It is neccessary, as it performs type erasure. That allows us to store all of them in one map regardless of the arguments types. However pointer destructor will be called with proper deleter which is stored internally. More info: Stackoverflow std::shared_ptr
class Event {
public:
template<typename... Args>
size_t addFunction(uint64_t callback_id, void(*callback)(Args...), Args... args) {
std::shared_ptr<Functor<Args...>> ptr(new Functor<Args...>(this->p_input, callback, args...));
std::map<uint64_t, std::shared_ptr<void>>::const_iterator it = p_functionMap.find(callback_id);
if (it == p_functionMap.end()) {
p_functionMap[callback_id] = ptr;
return ++p_functionIndex;
} else {
std::cout << "WE HAVE THIS CALLBACK ALREADY!: " << callback_id << std::endl;
}
return p_functionIndex;
}
size_t removeFunction(uint64_t callback_id) {
p_functionIndex -= p_functionMap.erase(callback_id);
return p_functionIndex;
}
private:
explicit Event(Buffer* input) :
p_input(input) {
p_functionIndex = 0;
}
private:
std::atomic<size_t> p_functionIndex;
std::map<uint64_t, std::shared_ptr<void>> p_functionMap;
Buffer* p_input;
};
Here is sketch of templated class holding all neceessary information about our functor. We use parameter packs to avoid defining arguments types. Arguments are store in std::tuple which allows us to either use them later while invoking callback function as well as swap some of them with new ones(eg. OpenCL callbacks). Every essential operation on arguments can be performed inside destructor. After that callback is raised inside destructor and, well, that's all folks! More info: Stackoverflow unpack parameter packs to call function pointer
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
swapArguments(std::tuple<Tp...>& t, cccl::Buffer* input) { }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if < I < sizeof...(Tp), void>::type
swapArguments(std::tuple<Tp...>& t, cccl::Buffer* input) {
using ARG = std::remove_reference<decltype(std::get<I>(t))>::type;
/*
HERE NEW TUPLE AND SWAP ARGUMENTS
OR ANYTHING ELSE(FOR EXAMPLE BUFFER DATA
LIKE BELOW)
*/
std::get<I>(t) = p_input->pull<ARG>();
swapArguments<I + 1, Tp...>(t, input);
}
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> {};
template<int ...S> struct gens<0, S...> { typedef seq<S...> type; };
template<typename... Args>
class Functor {
std::tuple<Args...> arguments;
void(*callback)(Args...);
Buffer *p_input;
public:
void dispatchCallback()
{
return callbackFunction(typename gens<sizeof...(Args)>::type());
}
template<int ...S>
void callbackFunction(seq<S...>)
{
return this->callback(std::get<S>(this->arguments) ...);
}
Functor(Buffer *input, void(*callback)(Args...), Args... args) {
this->p_input = input;
this->arguments = std::make_tuple(args...);
this->callback = callback;
}
~Functor() {
swapArguments(this->arguments, this->p_input);
this->dispatchCallback();
}
};
I hope I've explained everything properly. If more detailed description is neccessary please let me know, I'll try to expand my answer. Happy new year!

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()
}
}

Variadic members in non-template class

I'm trying to write a class Invocation which has a templated constructor:
template<typename F>
class Invocation {
public:
template<typename... Args>
Invocation(F&& f, Args&&... args)
{ /* store f and args somewhere for later use */ }
...
};
Normally I would parameterize the Invocation class itself with both F and Args..., but in this case I need a uniform type for a given F, so I'm trying to find a way to store args... of any types inside a Invocation<F>, and to incur as little performance hit as possible. (This might not be the best design, but it can be an interesting exercise.)
One thought is to use virtual functions:
template<typename F>
class ArgsBase {
public:
// discard return value
virtual void invoke(F&& f) = 0;
};
template<typename F, typename... Ts>
class Args : public ArgsBase<F> {
public:
Args(Ts&&... args) : args_(std::forward<Ts>(args)...) {}
void invoke(F&& f) override
{
/* somehow call f with args_ (something like std::apply) */
...
}
private:
std::tuple<Ts&&...> args_;
};
And then in the Invocation<F> class, we can for example have an std::unique_ptr<ArgsBase<F>> member, which points to an Args<F, Ts...> object created in the Invocation<F> ctor. And we can call its invoke virtual method when needed.
This is just one random idea I came up with. Is there any other way to achieve this? Ideally without the overhead of virtual functions or anything like that?
UPDATE: Thanks to the comments/answers that suggest using std::function or lambdas. I should've made it clear that I'm actually interested in a more general case, i.e., the variadic stuff might not be arguments to a callable. It can be just anything that I want to store in a class whose type is not parameterized by the types of these stuff.
As mentioned in comment, I wouldn't worry about storing arguments by value. The compiler's copy-elision can be generous.
Particularly if you offer the class an r-value invoke:
#include <tuple>
template<typename F>
class ArgsBase {
public:
// discard return value
virtual void invoke(F&& f) const & = 0;
virtual void invoke(F&& f) && = 0;
};
template<typename F, class... FunctionArgs>
class Args : public ArgsBase<F> {
public:
template<class...Ts>
Args(Ts&&... args) : args_(std::forward<Ts>(args)...) {}
template<std::size_t...Is, class Tuple>
static void invoke_impl(F& f, std::index_sequence<Is...>, Tuple&& t)
{
f(std::get<Is>(std::forward<Tuple>(t))...);
}
void invoke(F&& f) const & override
{
invoke_impl(f,
std::make_index_sequence<std::tuple_size<tuple_type>::value>(),
args_);
/* somehow call f with args_ (something like std::apply) */
}
void invoke(F&& f) && override
{
invoke_impl(f,
std::make_index_sequence<std::tuple_size<tuple_type>::value>(),
std::move(args_));
/* somehow call f with args_ (something like std::apply) */
}
private:
using tuple_type = std::tuple<FunctionArgs...>;
tuple_type args_;
};
template<class Callable, class...MyArgs>
auto later(MyArgs&&...args) {
return Args<Callable, std::decay_t<MyArgs>...>(std::forward<MyArgs>(args)...);
}
void foo(const std::string&, std::string)
{
}
int main()
{
auto l = later<decltype(&foo)>(std::string("hello"), std::string("world"));
l.invoke(foo);
std::move(l).invoke(foo);
}
If you're trying to save a function call with its parameters for later invocation, you could use lambdas packaged in std::function objects:
template<typename F, typename ... Args>
std::function<void()> createInvocation(F f, const Args& ... args)
{
return [f,args...]() { f(args...); };
}
Then you could use it like this:
void myFunc(int a, int b)
{
std::cout << "Invoked: " << a + b << std::endl;
}
int main() {
auto invocation = createInvocation(myFunc, 1, 2);
invocation();
return 0;
}
UPDATE: If you wanted to create a generic non-templated container type, you can wrap a tuple into a type that itself derives from a non-templated type. The main problem then is accessing the underlying data. This can be solved by creating a static function dispatch table that for a given tuple type, redirects queries so that std::get, which requires a compile-time constant index template parameter, can instead be invoked with a dynamically provided function parameter. Here is an implementation that achieves this:
class GenericTupleContainer
{
public:
virtual const void* getItemAtIndex(size_t index) = 0;
};
template<typename ... T>
class TupleContainer : public GenericTupleContainer
{
public:
TupleContainer(T&& ... args)
: data(std::forward<T>(args)...)
{}
const void* getItemAtIndex(size_t index) override
{
if(index >= sizeof...(T))
throw std::runtime_error("Invalid index");
return dispatchTable[index](data);
}
private:
template<size_t index>
static const void* getItemAtIdx(const std::tuple<T...>& data)
{
return &std::get<index>(data);
}
using GetterFn = const void*(*)(const std::tuple<T...>&);
static GetterFn* initDispatchTable()
{
static GetterFn dispatchTable[sizeof...(T)];
populateDispatchTable<sizeof...(T)>(dispatchTable, std::integral_constant<bool, sizeof...(T) == 0>());
return dispatchTable;
}
static GetterFn* dispatchTable;
template<size_t idx>
static void populateDispatchTable(GetterFn* table, std::false_type);
template<size_t idx>
static void populateDispatchTable(GetterFn* table, std::true_type)
{
//terminating call - do nothing
}
std::tuple<T...> data;
};
template<typename ... T>
typename TupleContainer<T...>::GetterFn* TupleContainer<T...>::dispatchTable = TupleContainer<T...>::initDispatchTable();
template<typename ... T>
template<size_t idx>
void TupleContainer<T...>::populateDispatchTable(GetterFn* table, std::false_type)
{
table[idx-1] = &TupleContainer<T...>::template getItemAtIdx<idx-1>;
populateDispatchTable<idx-1>(table, std::integral_constant<bool, idx-1 == 0>() );
}
template<typename ... T>
auto createTupleContainer(T&& ... args)
{
return new TupleContainer<T...>(std::forward<T>(args)...);
}
Then you can use the above as follows:
int main() {
GenericTupleContainer* data = createTupleContainer(1, 2.0, "Hello");
std::cout << *(static_cast<const int*>(data->getItemAtIndex(0))) << std::endl;
std::cout << *(static_cast<const double*>(data->getItemAtIndex(1))) << std::endl;
std::cout << (static_cast<const char*>(data->getItemAtIndex(2))) << std::endl;
return 0;
}
As you can see from the above usage, you've achieved the aim of wrapping an arbitrary templated tuple into a non-templated type, in such a way that you can access the component members with a normal (function) index parameter instead of a template one. Now the return type of such a getter has to be universal, so I've chosen to use void* here, which is not ideal. But you can develop this idea to make this container give more useful information about the types of its data tuple members. Also, note that this does use a virtual function. With some further work you can get rid of this as well, although you won't be able to get rid of at least one function pointer lookup (i.e. the lookup in the dispatch table) - this is the price paid for gaining the flexibility of being able to use a runtime value to index into the tuple.

lazy initialization with forwarding

Take a "lazy" constructor that might have the following interface:
template<class T>
struct LazyConstruct {
// accept any number of arguments,
// which would later be used to construct T
template<class... U>
LazyConstruct(U&&... u) {
// store the arguments somehow
}
T& get() {
if(!data) data.reset( new T( /* unpack the arguments */ ) );
return *data;
}
private:
std::unique_ptr<T> data;
};
What would be a nice way to implement this?
Here's a little bit of a convoluted way of doing what you want. The basic idea is to have LazyConstruct store the arguments pack in a tuple, and then unpack the tuple on demand to construct T.
template<class T, class... Args>
struct LazyConstruct {
// accept any number of arguments,
// which would later be used to construct T
template<class... U>
LazyConstruct(U&&... u)
: args(std::make_tuple(std::forward<U>(u)...))
{
}
T& get() {
if(!data) data = create(std::index_sequence_for<Args...>());
return *data;
}
template<std::size_t... I>
std::unique_ptr<T> create(std::index_sequence<I...>)
{
return std::unique_ptr<T>{new T(std::get<I>(args)...)};
}
private:
std::tuple<typename std::decay<Args>::type...> args;
std::unique_ptr<T> data;
};
I'm making use of C++14's std::index_sequence, if your standard library implementation does not ship this, then there are several examples on SO (this or this) showing how it can be implemented.
Finally a helper function template to construct LazyConstruct instances
template<class T, class... Args>
LazyConstruct<T, Args...> make_LazyConstruct(Args&&... args)
{
return LazyConstruct<T, Args...>{std::forward<Args>(args)...};
}
Live demo
Another version based on Alf's answer that uses std::function so that LazyConstruct's type doesn't change based on T's constructor signature.
template<class T>
struct LazyConstruct {
template<class... Args>
LazyConstruct(Args&&... args)
: holder([this, args = std::make_tuple(std::forward<Args>(args)...)]() {
return create(std::index_sequence_for<Args...>(), std::move(args));
})
{
}
T& get() {
if(!data) data = holder();
return *data;
}
template<std::size_t... I, class Tuple>
std::unique_ptr<T> create(std::index_sequence<I...>, Tuple args)
{
return std::unique_ptr<T>{new T(std::get<I>(args)...)};
}
private:
std::function<std::unique_ptr<T>()> holder;
std::unique_ptr<T> data;
};
Live demo
I am not sure about your question, but for lazy initialization I suggest you to use something along the lines of boost::optional<T>. You can delay initialization with it and you will not make use of a pointer and heap memory.
class MyClass {
public:
void f();
};
void anotherFunc(MyClass & c);
boost::optional<MyClass> mc; //Not initialized, empty, stack memory.
mc = MyClass{};
if (mc != boost::none)
mc->f();
anotherFunc(*mc);
Documentation is here: Boost.Optional
The easiest is probably to just capture the arguments in a lambda.
template<class T>
struct LazyConstruct {
// accept any number of arguments,
// which would later be used to construct T
template<class... U>
LazyConstruct(U&&... u)
: create( [=]() -> T* { return new T(u...); } )
{}
T& get() {
if(!data) data.reset( data.reset( create() ) );
return *data;
}
private:
std::unique_ptr<T> data;
std::function<auto()->T*> create;
};
Disclaimer: Code not touched by compiler's hands.
Note: While I'm unable right now to say exactly what's wrong with the idea (it's pretty late), lazy creation doesn't smell right, somehow. I suspect premature optimization.
As per the comment before. You want to delay and capture the arguments.
EDIT: Generalized solution, should work in C++11. Warnings: not tested. apply function is left as an exercise. See here for a possible implementation:
template <class T>
struct make {
template <class...Args>
T operator()(Args &&... args) const {
return T(std::forward<Args>(args)...);
}
};
template <class T, class... Args>
struct object_builder {
object_builder(Args... && args) :
captured_args_(std::forward<Args>(args)...) {}
T operator()() const {
return apply(make<T>{},
captured_args_);
}
private:
std::tuple<Args...> captured_args_;
};
template <class T, class...Args>
object_builder<T, Args...> make_object_builder(Args &&...args) {
return object_builder<T, Args...>(std::forward<Args>(args)...);
}
int main() {
//Create builders with captured arguments
auto scary_monster_builder =
make_object_builder<Monster>(scary, "big orc");
auto easy_monster_builder = make_object_builder<Monster>(easy,
"small orc");
//Instantiate objects with the captured arguments from before
auto a_scary_monster = scary_monster_builder();
auto an_easy_monster = easy_monster_builder();
}