C++ generic callback implementation - c++

I have a code that takes messages from flash player in a form of XML parse them into function and arguments and calls a registered callback for that function.
The piece of code that I want to replace is something nicely done (almost) generic Callback mechanism:
code for the generic callback implementation of flashSDK (ASInterface.inl).
The problem with it is that this code is written for flash and I want to replace the flash and use other service that will have the same interface. Is there any standard implementation of this callback mechanism (std? boost? something else open sourced?)?
This code implements generic callbacks mechanism that you can register function with number of arguments and types in a map:
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks["SomethingHappened"] = &SomethingHappened;
callbacks["SomethingElseHappened"] = &SomethingElseHappened;
and than search for it and call with an array of arguments:
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
HRESULT result = itCallback->second.Call(arguments, returnValue);
}
full usage example:
//init callbacks
std::map<std::wstring, Callback> callbacks;
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks[functionName] = &SomethingHappened;
void MessageArrived(string xmlInput)
{
string functionName = parseFunctionName(xmlInput);
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
//parse arguments
std::vector<std::wstring> args;
_Args::split(xml, args);
ASValue::Array arguments;
for (size_t i = 0, s = args.size(); i < s; ++i)
{
ASValue arg; arg.FromXML(args[i]);
arguments.push_back(arg);
}
ASValue returnValue;
//***this is where the magic happens: call the function***
HRESULT result = itCallback->second.Call(arguments, returnValue);
return result;
}
}

You probably need a wrapper around std::function, something like:
template <typename T> struct Tag{};
// Convert ASValue to expected type,
// Possibly throw for invalid arguments.
bool Convert(Tag<Bool>, AsValue val) { return (Boolean)val; }
int Convert(Tag<int>, AsValue val) { return (Number)val; }
// ...
struct Callback
{
private:
template <std::size_t ... Is, typename Ret, typename ... Ts>
static Ret call_impl(Ret(* func)(Ts...), std::index_sequence<Is...>)
{
if (arr.size() != sizeof...(Is)) throw std::invalid_argument{};
return func(Convert(tag<Ts>{}, arr[Is])...);
}
public:
template <typename Ret, typename ... Ts>
Callback(Ret(* func)(Ts...)) : Call{[func](ASValue::Array arr, ASValue& ret)
{
try
{
ret = Callback::call_impl(func, std::make_index_sequence<sizeof(...(Ts)>());
return S_OK;
} catch (...) {
return E_INVALIDARG;
}
}}
{}
std::function<HRESULT(ASValue::Array, ASValue&)> Call;
};
std::index_sequence is C++14, but you might find implementation on SO.

You could implement something like that.
A map of objects (GenericCallback here) containing std::function<R(Args...)> objects type-erased with std::any or std::variant.
You need to be careful in the way you call your function callbacks though.
E.g. I have to feed it a std::string("hello world") and not a simple C-string, otherwise the std::any_cast will throw (since a function<string(const char*)> is not a function<string(string)>).
#include <algorithm>
#include <any>
#include <functional>
#include <iostream>
#include <string>
#include <map>
#include <memory>
struct Caller {
virtual ~Caller() = default;
virtual std::any call(const std::vector<std::any>& args) = 0;
};
template<typename R, typename... A>
struct Caller_: Caller {
template <size_t... Is>
auto make_tuple_impl(const std::vector<std::any>& anyArgs, std::index_sequence<Is...> ) {
return std::make_tuple(std::any_cast<std::decay_t<decltype(std::get<Is>(args))>>(anyArgs.at(Is))...);
}
template <size_t N>
auto make_tuple(const std::vector<std::any>& anyArgs) {
return make_tuple_impl(anyArgs, std::make_index_sequence<N>{} );
}
std::any call(const std::vector<std::any>& anyArgs) override {
args = make_tuple<sizeof...(A)>(anyArgs);
ret = std::apply(func, args);
return {ret};
};
Caller_(std::function<R(A...)>& func_)
: func(func_)
{}
std::function<R(A...)>& func;
std::tuple<A...> args;
R ret;
};
struct GenericCallback {
template <class R, class... A>
GenericCallback& operator=(std::function<R(A...)>&& func_) {
func = std::move(func_);
caller = std::make_unique<Caller_<R, A...>>(std::any_cast<std::function<R(A...)>&>(func));
return *this;
}
template <class Func>
GenericCallback& operator=(Func&& func_) {
return *this = std::function(std::forward<Func>(func_));
}
std::any callAny(const std::vector<std::any>& args) {
return caller->call(args);
}
template <class R, class... Args>
R call(Args&&... args) {
auto& f = std::any_cast<std::function<R(Args...)>&>(func);
return f(std::forward<Args>(args)...);
}
std::any func;
std::unique_ptr<Caller> caller;
};
using namespace std;
//Global functions
int sub(int a, int b) { return a - b; }
std::function mul = [](int a, int b) { return a*b;};
std::string sortString(std::string str) {
std::sort(str.begin(), str.end());
return str;
}
int main()
{
std::map<std::string, GenericCallback> callbacks;
// Adding our callbacks
callbacks["add"] = [](int a, int b) { return a + b; };
callbacks["sub"] = sub;
callbacks["mul"] = std::move(mul);
callbacks["sortStr"] = sortString;
// Calling them (hardcoded params)
std::cout << callbacks["add"].call<int>(2, 3) << std::endl;
std::cout << callbacks["sub"].call<int>(4, 2) << std::endl;
std::cout << callbacks["mul"].call<int>(5, 6) << std::endl;
std::cout << callbacks["sortStr"].call<std::string>(std::string("hello world")) << std::endl;
// Calling "add" (vector of any params)
std::vector<std::any> args = { {1}, {2} };
std::any result = callbacks["add"].callAny(args);
std::cout << "result=" << std::any_cast<int>(result) << std::endl;
return 0;
}
https://godbolt.org/z/h63job

Related

Parameter pack expansion for variadic class member (tuple or other)

I am trying to store a tuple of references in a class (via a variadic template), and then I want to "loop" over them and assign them values.
Function process2 below works as expected, but I want to make function process1 work the same (making use of the stored references of the class itself). However, I can't make process1 compile. What's the correct way? Is there a way to maybe have member Args&... args; instead of std::tuple<Args&...> args (as that might allow the parameter expansion)? Any suggestions appreciated.
Sample Code:
#include <tuple>
#include <string>
#include <iostream>
template<class... Args>
class Handler
{
private:
template <class T>
static bool process_arg(T& val)
{
if constexpr (std::is_same_v<T, int>)
val = 123;
else if constexpr (std::is_same_v<T, std::string>)
val = "string";
else
{
// do something
return false;
}
return true;
}
public:
const std::tuple<Args&...> args;
Handler(Args&... args)
: args(args ...) { }
// bool process1() const
// {
// // Compile Error: operand of fold expression has no unexpanded parameter packs
// const bool success = (process_arg(args) && ...);
// // Compile Error: no matching function for process_arg(int&, int&, std::string&)
// bool success = true;
// std::apply([&success](auto &&... v) { success = success && process_arg(v...); }, args);
// return success;
// }
template<class... Args2>
static bool process2(Args2&... args2)
{
const bool success = (process_arg(args2) && ...);
return success;
}
};
int main()
{
int a, b;
std::string c;
// Handler(a, b, c).process1();
Handler<>::process2(a, b, c);
std::cout << a << "," << b << "," << c << "\n";
return 0;
}
You're on the right track with std::apply but the syntax is incorrect; the pack expansion needs to be outside the call to process_arg. Also, you don't need the variable success at all; you can use a fold-expression directly:
bool process1() const
{
return std::apply([](auto &&... v) {
return (process_arg(v) && ...);
}, args);
}
Here's a demo
Not sure this is what you need, since it doesn't store anything as members. But I think it gives the desired output. O well... maybe there is something for you to take away from this :)
class Handler
{
private:
template <class T>
static bool process_arg(T& val)
{
if constexpr (std::is_same_v<T, int>)
val = 123;
else if constexpr (std::is_same_v<T, std::string>)
val = "string";
else
{
// do something
return false;
}
return true;
}
public:
template<typename arg_t, typename... args_t>
static constexpr bool process(arg_t& arg, args_t&... args)
{
if constexpr (sizeof...(args_t) > 0)
{
bool success = process_arg<arg_t>(arg) && process(args...);
return success;
}
return process_arg<arg_t>(arg);
}
};
int main()
{
int a, b;
std::string c;
Handler::process(a, b, c);
std::cout << a << "," << b << "," << c << "\n";
return 0;
}

C++ templates to avoid long switches, while calling a function with different return types

I have many functions q1, q2, q3, etc., each with a different return type (int, int64_t, std::string, etc.).
I also have a print_result function that prints out their results (and the time they take to run, but trimmed here for simplicity):
template <typename T>
void print_result(T (*func)()) {
T res = func();
std::cout << res << std::endl;
}
I also have big switch statement to print the result for each of the functions:
switch (question_num) {
case 1: print_result(q1); break;
case 2: print_result(q2); break;
case 3: print_result(q3); break;
// ...
}
Objective: I would like to replace this switch statement with a template function, to avoid copying each line every time I add a new function.
I have tried to look at C++ template instantiation: Avoiding long switches, but I'm new to template metaprogramming, so not sure how to handle this exactly.
My current attempt that doesn't compile:
template <<int, typename> ...> struct FuncList {};
template <typename T>
bool handle_cases(int, T, FuncList<>) {
// default case
return false;
}
template <<int I, typename T> ...S>
bool handle_cases(int i, T (*func)(), FuncList<T, S...>) {
if (I != i) {
return handle_cases(i, func, FuncList<S...>());
}
print_result(func);
return true;
}
template <typename ...S>
bool handle_cases(int i, T (*func)()) {
return handle_cases(i, func, FuncList<S...>());
}
// ...
bool res = handle_cases<
<1, q1>, <2, q2>, <3, q3>
>(question_num);
// ...
My ideal way of using this template is shown at the last line there.
Note that the mappings from the function number to the function is provided there. The function numbers are fixed, i.e. q1 maps to the constant 1 and that won't change at runtime.
The compilation error (it might be rather basic but I really don't know much about metaprogramming):
error: expected unqualified-id before ‘<<’ token
17 | template <<int, typename> ...> struct FuncList {};
| ^~
If you can use c++17, here's a "simplified" version of #Klaus's approach. Instead of using a had-made recursive structure, you could use a c++17 fold-expression:
template<auto... Funcs, std::size_t... I>
bool select_case(std::size_t i, std::integer_sequence<std::size_t, I...>) {
return ([&]{ if(i == I) { print_result(Funcs); return true; } return false; }() || ... );
}
template<auto... Funcs>
struct FuncSwitch {
static bool Call(std::size_t i) {
return select_case<Funcs...>(i, std::make_index_sequence<sizeof...(Funcs)>());
}
};
The idea is to wrap each of Funcs in a lambda such that only the function corresponding to the index passed is called. Note that the || in the fold expression short-circuits.
Would be used like this:
float q0() { return 0.f; }
int q1() { return 1; }
std::string q2() { return "two"; }
int main() {
bool success = FuncSwitch<q0, q1, q2>::Call(1);
}
See here for a complete example.
I've got a different proposal:
Use an std::array instead of switch (or std::map if the switch cases are non-continuous, std::array has O(1) access time, std::map O(log(n)) and switch O(n).
Use std::function and std::bind to bind your functions you want to call to a functor object
use the index into the array to call the function
Use placeholders if you need to pass additional data
#include <iostream>
#include <functional>
template <typename T>
void print_result(T (*func)()) {
T res = func();
std::cout << res << std::endl;
}
int int_function() {
return 3;
}
double double_function() {
return 3.5;
}
std::array<std::function<void()>, 2> functions({
std::bind(print_result<int>, int_function),
std::bind(print_result<double>, double_function),
});
int main() {
functions[0]();
functions[1]();
return 0;
}
Output:
3
3.5
See: Why does std::function can implicit convert to a std::function which has more parameter?
Update:
With parameter passing:
#include <iostream>
#include <functional>
template <typename T>
void print_result(T (*func)(int), int value) {
T res = func(value);
std::cout << res << std::endl;
}
int int_function(int value) {
return 3 * value;
}
double double_function(int value) {
return 3.5 * value;
}
std::array<std::function<void(int)>, 2> functions({
std::bind(print_result<int>, int_function, std::placeholders::_1),
std::bind(print_result<double>, double_function, std::placeholders::_1),
});
int main() {
functions[0](10);
functions[1](11);
return 0;
}
Output:
30
38.5
You may like a version which do not need any kind of runtime containers, did not generate any objects in between and even do not generate a data table and generates very less code and is also easy to use:
// Example functions
int fint() { return 1; }
double fdouble() { return 2.2; }
std::string fstring() { return "Hallo"; }
// your templated result printer
template < typename T>
void print_result( T parm )
{
std::cout << "The result of call is " << parm << std::endl;
}
// lets create a type which is able to hold functions
template < auto ... FUNCS >
struct FUNC_CONTAINER
{
static constexpr unsigned int size = sizeof...(FUNCS);
};
// and generate a interface to switch
template < unsigned int, typename T >
struct Switch_Impl;
template < unsigned int IDX, auto HEAD, auto ... TAIL >
struct Switch_Impl< IDX, FUNC_CONTAINER<HEAD, TAIL...>>
{
static void Do( unsigned int idx )
{
if ( idx == IDX )
{
// Your function goes here
print_result(HEAD());
}
else
{
if constexpr ( sizeof...(TAIL))
{
Switch_Impl< IDX+1, FUNC_CONTAINER<TAIL...>>::Do(idx);
}
}
}
};
// a simple forwarder to simplify the interface
template < typename T>
struct Switch
{
static void Do(unsigned int idx )
{
Switch_Impl< 0, T >::Do( idx );
}
};
// and lets execute the stuff
int main()
{
using FUNCS = FUNC_CONTAINER< fint, fdouble, fstring >;
for ( unsigned int idx = 0; idx< FUNCS::size; idx++ )
{
Switch<FUNCS>::Do(idx);
}
}
Given you "current attempt"... it seems to me that you could write a handle_cases struct/class almost as follows
struct handle_cases
{
std::map<int, std::function<void()>> m;
template <typename ... F>
handle_cases (std::pair<int, F> const & ... p)
: m{ {p.first, [=]{ print_result(p.second); } } ... }
{ }
void operator() (int i)
{ m[i](); }
};
with a map between an integer and a lambda that call print_result with the function and an operator() that call the requested lambda, given the corresponding index.
You can create an object of the class as follows (unfortunately I don't see a way to avoid the std::make_pair()s)
handle_cases hc{ std::make_pair(10, q1),
std::make_pair(20, q2),
std::make_pair(30, q3),
std::make_pair(40, q4) };
and using it as follows
hc(30);
The following is a full compiling example
#include <functional>
#include <map>
#include <iostream>
template <typename T>
void print_result (T(*func)())
{
T res = func();
std::cout << res << std::endl;
}
struct handle_cases
{
std::map<int, std::function<void()>> m;
template <typename ... F>
handle_cases (std::pair<int, F> const & ... p)
: m{ {p.first, [=]{ print_result(p.second); } } ... }
{ }
void operator() (int i)
{ m[i](); }
};
char q1 () { return '1'; }
int q2 () { return 2; }
long q3 () { return 3l; }
long long q4 () { return 4ll; }
int main ()
{
handle_cases hc{ std::make_pair(10, q1),
std::make_pair(20, q2),
std::make_pair(30, q3),
std::make_pair(40, q4) };
hc(30);
}

Event class needs updating

I have an Event class that is written in half C and half C++11.
It currently does not work with lambdas nor std::functions, only free functions or member functions.
It is very hard to use properly (I've never gotten the plain Subscribe method to compile when used) and the use of void* and raw function pointers is just gross.
I'd like to get it up-to-date in terms of C++17 with proper variadic template types, working with lambdas and std::function's and hopefully only have one public set of subscribe/unsubscribe methods that just work with anything I give it.
Event.hpp
#pragma once
#include <vector>
template <typename... ARGS>
class Event {
public:
struct event_sub_t;
using cb_t = void(*)(event_sub_t*, ARGS...);
using cb_with_arg_t = void(*)(void*, ARGS...);
struct event_sub_t {
cb_t cb;
void *secondary_cb;
void *user_arg;
};
Event() = default;
~Event() = default;
void Subscribe(void *user_arg, cb_with_arg_t cb) {
event_sub_t sub;
sub.cb = FunctionWithArgumentCallback;
sub.secondary_cb = cb;
sub.user_arg = user_arg;
subscriptions.push_back(sub);
}
void Unsubscribe(void *user_arg, void* cb) {
subscriptions.erase(std::remove_if(std::begin(subscriptions),
std::end(subscriptions),
[&cb, &user_arg](const event_sub_t& sub) {
return (sub.secondary_cb == cb) && (sub.user_arg == user_arg);
}),
std::end(subscriptions));
}
void Unsubscribe_by_argument(void *user_arg) {
subscriptions.erase(std::remove_if(std::begin(subscriptions),
std::end(subscriptions),
[&user_arg](const event_sub_t& sub) {
return sub.user_arg == user_arg;
}),
std::end(subscriptions));
}
template <typename T>
void Subscribe_method(T *obj, void (T::*mcb)(ARGS...)) {
event_sub_t sub;
sub.cb = MethodCallback<T, decltype(mcb)>;
sub.secondary_cb = *(void**)(&mcb);
sub.user_arg = obj;
subscriptions.push_back(sub);
}
template <typename T>
void Unsubscribe_method(T *obj, void (T::*mcb)(ARGS...)) {
Unsubscribe(obj, *(void**)&mcb);
}
template <typename T>
void Unsubscribe_object(T *obj) {
Unsubscribe_by_argument(obj);
}
void Trigger(ARGS... args) {
for(auto& sub : subscriptions) {
sub.cb(&sub, std::forward<ARGS>(args)...);
}
}
private:
std::vector<event_sub_t> subscriptions;
static void FunctionWithArgumentCallback(event_sub_t *sub, ARGS... args);
template <typename T, typename MCB>
static void MethodCallback(event_sub_t *sub, ARGS... args);
};
template <typename ...ARGS>
void Event<ARGS...>::FunctionWithArgumentCallback(event_sub_t *sub, ARGS... args) {
cb_with_arg_t cb = (cb_with_arg_t)(sub->secondary_cb);
cb(sub->user_arg, std::forward<ARGS>(args)...);
}
template <typename ...ARGS>
template <typename T, typename MCB>
void Event<ARGS...>::MethodCallback(event_sub_t *sub, ARGS... args) {
MCB mcb = *(MCB*)&(sub->secondary_cb);
T *obj = (T*)(sub->user_arg);
(obj->*mcb)(std::forward<ARGS>(args)...);
}
Current Usage:
class Foo {
public:
//...
void Update() { OnEventFoo.Trigger(text); }
Event<const std::string&> OnEventFoo{};
private:
std::string text{};
};
//Foo::Update is called somewhere in other code...
//Bar subscribes/unsubscribes to Foo's event.
//Doesn't have to be RAII, can be as simple as putting
//the subscribe/unsubscribe calls before and after some other function call.
class Bar {
public:
std::string text{};
explicit Bar(Foo& foo)
: _foo(foo)
{
foo.OnEventFoo.Subscribe_method(this, &Bar::Thing2);
}
~Bar() {
foo.OnEventFoo.Unsubscribe_method(this, &Bar::Thing2);
}
void Thing2(const std::string& text) {
std::cout << "Calling " << __FUNCTION__ << " with " << text;
}
private:
Foo _foo{};
};
Intended Usage:
//...Foo and Bar classes and stuff
static auto bar_lambda = [bar](const std::string& text){ bar.Thing2(text) };
foo.Subscribe(bar_lambda, "Hello Bar!");
foo.Subscribe(Bar::Thing2, bar.text);
foo.Subscribe(FreeOrStdFunction, "Free Bar!");
//...
foo.Unsubscribe(Bar::Thing2);
foo.Unsubscribe(FreeFunction);
foo.Unsubscribe(bar_lambda);
Not sure to understand what do you need.
But seems to me that you need std::bind().
Anyway... if arguments for a single callable are passed in Subscribe(), it seems to me that Event doesn't needs to be a template class anymore and that the std::vector of std::function is something as follows
private:
std::vector<std::function<void()>> subsV;
I mean: a vector of std::function's of type void().
You can populate it through the following method
template <typename F, typename ... Args>
std::size_t Subscribe (F const & f, Args const & ... as)
{
subsV.emplace_back(std::bind(f, as...));
return subsV.size() - 1u;
}
Observe that with a simple callable (not non-static class/struct method) you have to call it passing first the callable and next the arguments
auto i1 = e.Subscribe(
[](int, long){ std::cout << "l1" << std::endl; }, 0, 1l);
but calling it with a non-static method you have to pass first the pointer to the method, second a object or a pointer to a object (works in both cases) to of the class and last the arguments for the method.
foo f;
// ...............................V works with objects
auto i2 = e.Subscribe(&foo::func, f, "string 1");
auto i3 = e.Subscribe(&foo::funv, &f, "string 2");
// ...............................^^ and works with pointers
For Unsuscribe() i suggest to pass the index of the subscription (returned by Subscribe()
void Unsubscribe (std::size_t idx)
{ subsV.at(idx) = nullptr; }
and the Trigger() simply become
void Trigger ()
{
for ( auto & sub : subsV )
if ( sub )
sub();
}
The following is a full compiling example (should works also with C++11)
#include <vector>
#include <iostream>
#include <functional>
class Event
{
private:
std::vector<std::function<void()>> subsV;
public:
Event() = default;
~Event() = default;
template <typename F, typename ... Args>
std::size_t Subscribe (F const & f, Args const & ... as)
{
subsV.emplace_back(std::bind(f, as...));
return subsV.size() - 1u;
}
void Unsubscribe (std::size_t idx)
{ subsV.at(idx) = nullptr; }
void Trigger ()
{
for ( auto & sub : subsV )
if ( sub )
sub();
}
};
struct foo
{
void func (std::string const & s)
{ std::cout << "foo::func(): " << s << std::endl; }
};
int main()
{
Event e;
foo f;
auto i1 = e.Subscribe(
[](int, long){ std::cout << "l1" << std::endl; }, 0, 1l);
auto i2 = e.Subscribe(&foo::func, f, "string 1");
auto i3 = e.Subscribe(&foo::func, &f, "string 2");
e.Trigger();
e.Unsubscribe(i2);
e.Trigger();
e.Unsubscribe(i1);
e.Trigger();
e.Unsubscribe(i3);
e.Trigger();
}

How do I generalize calling a list of functions in C++?

I have the following code which allows me to instantiate and then call a list of void() functions.
(I am using https://github.com/philsquared/Catch for unit testing if you wish to compile and run this code).
#include "catch.hpp"
#include <functional>
#include <vector>
class ChainOfResponsibility : public std::vector<std::function<void()> >, public std::function<void()>
{
public:
void operator()() const
{
for(std::vector<std::function<void()> >::const_iterator it = begin(); it != end(); ++it) {
(*it)();
}
}
};
TEST_CASE("ChainOfResponsibility calls its members when invoked")
{
bool test_function_called = false;
std::function<void()> test_function = [&]()
{
test_function_called = true;
};
ChainOfResponsibility object_under_test;
object_under_test.push_back(test_function);
object_under_test();
REQUIRE(test_function_called);
}
My question is how do I template the ChainOfResponsibility class to accept functions with a different (but consistent) signature?
For example, consider a ChainOfResponsibility<void(int)> or a ChainOfResponsibility<ReturnClass(Argument1Class, Argument2Class)>.
For the sake of argument, lets say that the second example returns the value returned by the last member in the chain, or the default value for ReturnClass if the chain is empty.
Also, if the STL already contains a template class that achieves this, then I would prefer to use it over my home-grown class.
Your specific "discard all the intermediate results" is also fairly simple, but I think it's a bad idea.
template<typename Ret, typename ... Args>
class ChainOfResponsibility
{
std::vector<std::function<Ret(Args...)> > chain;
public:
Ret operator()(Args ... args) const
{
Ret value;
for(auto & func : chain) {
value = func(args...);
}
return value;
}
};
void has to be treated on it's own
template<typename ... Args>
class ChainOfResponsibility<void, Args...>
{
std::vector<std::function<void(Args...)> > chain;
public:
void operator()(Args ... args) const
{
for(auto & func : chain) {
func(args...);
}
}
};
Note that deriving from std:: types is a bad idea, especially std::function, which is a type-erasing callable, not "the base of all callables". You can simply provide an operator()
options for improving the non-void case:
// fold the results
template <typename BinaryFunction>
Ret operator()(Args ... args, BinaryFunction add, Ret init) const
{
for(auto & func : chain) {
init = add(init, func(args...));
}
return init;
}
// return a vector
template <typename BinaryFunction>
std::vector<Ret> operator()(Args ... args) const
{
std::vector<Ret> results(chain.size());
for(auto & func : chain) {
results.push_back(func(args...));
}
return results;
}
You don't need to use the std::function as a base class, using std::vector is sufficent. The template ChainOfResponsibility can use the same template paramter list as the std::function like follows:
#include <iostream>
#include <string>
#include <functional>
#include <vector>
template<typename>
class ChainOfResponsibility;
template<typename R, typename... Args>
class ChainOfResponsibility<R(Args...)> :
public std::vector<std::function<R(Args...)>> {
public:
R operator()(const Args&... args) {
R result {};
for(auto it = this->begin(); it != this->end(); ++it)
result = (*it)(args...);
return result;
}
};
int main() {
ChainOfResponsibility<std::string(int, int)> tester;
tester.push_back([](int a, int b)->std::string {
return std::to_string(a + b);
});
std::cout << tester(4, 2) << std::endl;
}
Anyway, using std::vector only is good enoug for the problem you described. If the content of the overloaded operator() is nothing special, you can change my example above as follows:
int main() {
std::vector<std::function<std::string(int, int)>> tester;
tester.push_back([](int a, int b)->std::string {
return std::to_string(a + b);
});
std::string result;
for(auto& test_fn : tester)
result = test_fn(4, 2);
std::cout << result << std::endl;
}
You also can write a function template instead of overloading the operator():
template<typename R, typename... Args>
R perform(const std::vector<std::function<R(Args...)>>& functions,
const Args&... args) {
R result {};
for(auto& test_fn : functions)
result = test_fn(4, 2);
return result;
}

How to handle an api which returns different data types for the same input data types?

How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.