Very specific sub-class method call - c++

I have this design:
class GenericData
{
};
class Data1 : public GenericData
{
};
class Data2 : public GenericData
{
};
class CompBase
{
public:
void process()
{
// inputs are check to make sure there number and order is correct
// Use them to automatically call the correct function
// What here ?
}
vector<GenericData> inputs;
};
class Comp1 : public CompBase
{
public:
void compute(Data1 input1, Data1 input2) { cout << "Comp1::compute(Data1 input1, Data1 input2)" << endl; }
void compute(Data2 input1, Data2 input2) { cout << "Comp1::compute(Data2 input1, Data2 input2)" << endl; }
void compute(Data1 input1, Data2 input2) { cout << "Comp1::compute(Data1 input1, Data2 input2)" << endl; }
};
class Comp2 : public CompBase
{
public:
void compute(Data1 input1) { cout << "Comp2::compute(Data1 input1)" << endl; }
void compute(Data2 input1) { cout << "Comp2::compute(Data2 input1)" << endl; }
};
With the following constraints:
The compute functions must be called from GenericComp but can't be all declared here because there would be two many (Data1,2 and Comp1,2 are just examples)
I must be able to have a collection of CompBase
The compute functions must not have to check their inputs (i.e. passing them the same structure is not possible)
The code must be generic enough to allow addition of other Data, Comp and compute easily
Here is an example of use:
int main() {
Data1 d1; Data2 d2;
Comp1 c1; Comp2 c2;
c1.inputs = { d1, d1 };
c1.process(); // "Comp1::compute(Data1 input1, Data1 input2)"
c1.inputs = { d2, d2 };
c1.process(); // "Comp1::compute(Data2 input1, Data2 input2)"
c1.inputs = { d1, d2 };
c1.process(); // "Comp1::compute(Data1 input1, Data2 input2)"
vector<GenericComp> comps = { c1, c2 };
for (comp : comps)
{
comp.process();
}
return 0;
}
I have a "working" example of this here.
I tried different approaches: CRTP, variadic template functions, currying and partial application and a lot of googling but I'm stuck here.
Is it possible with these constraints ? If so how could you do that ?

Thank you guys for the answers.
#Daniel Jour, your post really helped me and I had few modifications to make to fit my case.
Here is an updated example that will work for me.
#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <memory>
using namespace std;
class GenericData
{
public:
virtual ~GenericData() {};
};
class Data1 : public GenericData
{
public:
virtual ~Data1() {};
};
class Data2 : public GenericData
{
public:
virtual ~Data2() {};
};
class GenericComp
{
public:
virtual ~GenericComp() {};
vector<GenericData*> inputs;
};
class Comp1 : public GenericComp
{
public:
static bool compute(shared_ptr<Data1> const & input1, shared_ptr<Data1> const & input2) { cout << "Comp1::compute(Data1 input1, Data1 input2)" << (input2 ? "ok" : "null") << endl; return true; }
static bool compute(shared_ptr<Data2> const & input1, shared_ptr<Data2> const & input2) { cout << "Comp1::compute(Data2 input1, Data2 input2)" << endl; return true; }
static bool compute(shared_ptr<Data1> const & input1, shared_ptr<Data2> const & input2) { cout << "Comp1::compute(Data1 input1, Data2 input2)" << endl; return true; }
};
class Comp2 : public GenericComp
{
public:
static bool compute(shared_ptr<Data1> const & input1) { cout << "Comp2::compute(Data1 input1)" << endl; return true; }
static bool compute(shared_ptr<Data2> const & input1) { cout << "Comp2::compute(Data2 input1)" << endl; return true; }
};
// Arguments type to the function "interface"
using Arguments = std::vector<shared_ptr<GenericData>> const &;
// the interface
using Function = std::function<bool (Arguments)>;
// Base case of packing a function.
// If it's taking a vector and no more
// arguments, then there's nothing left to
// pack.
template<std::size_t N, typename Fn>
Function pack(Fn && fn)
{
return [fn = std::forward<decltype(fn)>(fn)] (Arguments arguments)
{
if (N != arguments.size())
{
throw std::string{"wrong number of arguments, expected "} +
std::to_string(N) +
std::string{" but got "} +
std::to_string(arguments.size());
}
return fn(arguments);
};
}
// pack a function to a function that takes
// it's arguments from a vector, one argument after
// the other.
template<std::size_t N, typename Arg, typename... Args, typename Fn>
Function pack(Fn && fn)
{
return pack<N+1, Args...>([fn = std::forward<decltype(fn)>(fn)] (Arguments arguments, Args const &... args)
{
try
{
return fn(arguments, arguments.at(N), args...);
}
catch (std::bad_cast const &)
{
throw std::string{"argument "} + std::to_string(N) + std::string{" has wrong type "};
}
});
}
// transform a function into one that takes its
// arguments from a vector
template<typename... Args, typename Fn>
Function pack_function(Fn && fn)
{
return pack<0, Args...>([fn = std::forward<decltype(fn)>(fn)] (Arguments arguments, Args const &... args) -> bool
{
return fn(args...);
});
}
int main() {
// Pack all the functions
std::map<std::string, Function> operations;
operations["Comp1_Data1_Data1"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
{
return Comp1::compute(dynamic_pointer_cast<Data1>(i1), dynamic_pointer_cast<Data1>(i2));
});
operations["Comp1_Data2_Data2"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
{
return Comp1::compute(dynamic_pointer_cast<Data2>(i1), dynamic_pointer_cast<Data2>(i2));
});
operations["Comp1_Data1_Data2"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
{
return Comp1::compute(dynamic_pointer_cast<Data1>(i1), dynamic_pointer_cast<Data2>(i2));
});
operations["Comp2_Data1"] = pack_function<shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1)
{
return Comp2::compute(dynamic_pointer_cast<Data1>(i1));
});
operations["Comp2_Data2"] = pack_function<shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1)
{
return Comp2::compute(dynamic_pointer_cast<Data2>(i1));
});
// Create the possible inputs
vector<shared_ptr<GenericData>> data1_data1 { shared_ptr<Data1>(), shared_ptr<Data1>() };
vector<shared_ptr<GenericData>> data2_data2 { shared_ptr<Data2>(), shared_ptr<Data2>() };
vector<shared_ptr<GenericData>> data1_data2 { shared_ptr<Data1>(), shared_ptr<Data2>() };
vector<shared_ptr<GenericData>> data1 { shared_ptr<Data1>() };
vector<shared_ptr<GenericData>> data2 { shared_ptr<Data2>() };
// The calls !
operations["Comp1_Data1_Data1"](data1_data1);
operations["Comp1_Data2_Data2"](data2_data2);
operations["Comp1_Data1_Data2"](data1_data2);
operations["Comp2_Data1"](data1);
operations["Comp2_Data2"](data2);
// Wrong arguments
try
{
operations["Comp1_Data1_Data1"](data1);
}
catch (std::string const & e)
{
cout << e << endl;
}
try
{
operations["Comp2_Data1"](data1_data1);
}
catch (std::string const & e)
{
cout << e << endl;
}
return 0;
}

Related

For-loop with conditional call of a variadic amount of functions

Instead of performing recursive calls, I want an arbitrary number of functions to trigger based on some condition in a loop.
How do I transform method0, method1 to be called for a variadic number of typename ... methods?
Edit:
As you cannot return a variadic number of types, I modified the example a litte.
template <typename method0, typename method1, typename ... Args>
void loop(Args ... args)
{
for (int i = 0; i < iter_max; i++)
{
if (method0::condition(args))
{
method0::call_modify(args);
}
if (method1::condition(args))
{
method1::call_modify(args);
}
//...
}
}
template<typename ... Args>
struct method0
{
static bool condition(Args& ... args)
{
//Trigger condition
}
static void call_modify(Args& ... args)
{
//Perform action on args
}
};
You can do it like this:
#include <iostream>
template<typename ... Args>
struct method0
{
static bool condition(Args& ... args)
{
std::cout << "condition \n";
return true;
}
static void call(Args& ... args)
{
std::cout << "call\n";
}
};
template <typename... Methods, typename ... Args>
void loop(Args ... args)
{
auto apply = []<typename m>(auto&... a){
if (m::condition(a...)) m::call(a...);
};
(apply.template operator()<Methods>(args...), ...);
}
int main() {
loop<method0<int,int>,method0<int,int>>(1,2);
}
Output:
condition
call
condition
call
Perhaps loop should take references too in that case. And I ommitted the loop, it should be trivial to add it.
This is the old version of the answer for the slightly simpler case of non-variadic condition and call:
struct A {
static bool condition(int x) {
std::cout << "A::condition " << x << "\n";
return true;
}
static int call(int) {
std::cout << "A::call\n";
return 42;
}
};
struct B {
static bool condition(int x) {
std::cout << "B::condition " << x << "\n";
return false;
}
static int call(int) {
std::cout << "B::call\n";
return 0;
}
};
You can make loop call n-th types condition and call with n-th args like this:
template <typename... Methods, typename ... Args>
void loop(Args ... args)
{
auto apply = []<typename m>(auto& a){
if (m::condition(a)) a = m::call(a);
};
(apply.template operator()<Methods>(args), ...);
}
int main() {
loop<A,B>(1,2);
}
Output:
A::condition 1
A::call
B::condition 2
I managed to generalize 463035818_is_not_a_number's answer to be looped, and to take on a variadic number of input arguments. Full working example:
#include <iostream>
struct method0
{
static bool condition(double &arg1, double &arg2)
{
// Trigger condition
return (arg1 <= arg2);
}
static void call_modify(double &&arg1, double &&arg2)
{
std::cout << "method0!" << std::endl;
arg1 += 1;
}
};
struct method1
{
static bool condition(double &arg1, double &arg2)
{
// Trigger condition
return (arg1 > arg2);
}
static void call_modify(double &&arg1, double &&arg2)
{
std::cout << "method1!" << std::endl;
arg1 -= 1;
}
};
template <int iter_max, typename... Methods>
struct wrapper
{
template <typename... Args>
static void loop(Args &...args)
{
auto apply = []<typename m>(auto&&... a)
{
if (m::condition(a...))
m::call_modify(std::forward<Args>(a)...);
};
for (int i = 0; i < iter_max; i++)
{
(apply.template operator()<Methods>(std::forward<Args>(args)...), ...);
}
}
};
int main()
{
double arg1 = 2.0;
double arg2 = 0.;
constexpr int iter_max = 5;
wrapper<iter_max, method0, method1>::loop<double, double>(arg1, arg2);
}
output:
method1!
method1!
method0!
method1!
method0!
method1!
method0!
method1!

What is a good way to cast and call variable argument functions from a map dynamically at runtime WITHOUT using an external library

Let's say I multiple functions with variable arguments:
void greetWorld() {
cout << "Hello World!" << endl;
}
void greetName(const string& name) {
cout << "Hello " << name << "!" << endl;
}
void printAddition(const int lhs, const int rhs) {
cout << "Addition: " << to_string(lhs + rhs) << endl;
}
And these are stored in a map of std::strings to functions (functions being stored as a polymorphic class).
template<typename... Args>
class DerivedFunction;
class BaseFunction {
public:
template<typename... Args>
void operator()(Args... args) const {
(*static_cast<const DerivedFunction<Args...>*>(this))(args...);
}
};
template<typename... Args>
class DerivedFunction : public BaseFunction {
public:
DerivedFunction(void(*function)(Args...)) {
this->function = function;
}
void operator()(Args... args) const {
function(args...);
}
private:
void(*function)(Args...);
};
template<typename... Args>
unique_ptr<DerivedFunction<Args...>> make_function(
void(*function)(Args...)
) {
return std::make_unique<DerivedFunction<Args...>>(function);
}
int main() {
unordered_map<string, unique_ptr<BaseFunction>> function_map;
function_map.insert({ "greetWorld", make_function(&greetWorld) });
function_map.insert({ "greetName", make_function(&greetName) });
function_map.insert({ "printAddition", make_function(&printAddition) });
...
}
I can call the functions at compile time like:
int main() {
...
(*function_map.at("greetWorld"))();
(*function_map.at("greetName"))("Foo"s);
(*function_map.at("printAddition"))(1, 2);
}
If I then have a string, or stream like:
greetWorld
greetName string Foo
printAddition int 1 int 2
What would be a good way to call the functions?
I can not figure out any way to cast a type at runtime.
Why?
I am trying to implement some kind of remote call procedure for learning purposes. I do not want to use an external library as I am trying to learn how to implement this with the C++ standard library for the sake of understanding C++ more.
What have I tried?
Not much. I've tested creating functions that take a std::vector of std::anys as an argument, and then had the function any_cast them to the type they are. Whilst this does work, it does not look nice, it requires duplicates of all functions, I would rather be able to write functions with meaningful arguments than ambigious.
Minimum Example
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
using namespace std;
void greetWorld() {
cout << "Hello World!" << endl;
}
void greetName(const string& name) {
cout << "Hello " << name << "!" << endl;
}
void printAddition(const int lhs, const int rhs) {
cout << "Addition: " << to_string(lhs + rhs) << endl;
}
template<typename... Args>
class DerivedFunction;
class BaseFunction {
public:
template<typename... Args>
void operator()(Args... args) const {
(*static_cast<const DerivedFunction<Args...>*>(this))(args...);
}
};
template<typename... Args>
class DerivedFunction : public BaseFunction {
public:
DerivedFunction(void(*function)(Args...)) {
this->function = function;
}
void operator()(Args... args) const {
function(args...);
}
private:
void(*function)(Args...);
};
template<typename... Args>
unique_ptr<DerivedFunction<Args...>> make_function(
void(*function)(Args...)
) {
return std::make_unique<DerivedFunction<Args...>>(function);
}
int main() {
unordered_map<string, unique_ptr<BaseFunction>> function_map;
function_map.insert({ "greetWorld", make_function(&greetWorld) });
function_map.insert({ "greetName", make_function(&greetName) });
function_map.insert({ "printAddition", make_function(&printAddition) });
cout << "Calling functions at compile time." << endl << endl;
(*function_map.at("greetWorld"))();
(*function_map.at("greetName"))("Foo"s);
(*function_map.at("printAddition"))(1, 2);
//cout << endl << "Calling functions at runtime." << endl << endl;
//string runtime =
// "greetWorld\n"
// "greetName string Foo\n"
// "printAddition int 1 int 2";
//
// todo: call functions
}
Solved.
If you apply the accepted solution, you can call functions from the text like I had wanted.
Here is new code for an example Tcp server and client. The client sends function names and arguments as a string to the server. The server then executes these. Exactly what I wanted.
struct FunctionNameAndArguments {
string function_name;
vector<RPC> arguments;
};
FunctionNameAndArguments parseFunctionNameAndArguments(
const string& function_name_and_arguments_string
) {
istringstream ss(function_name_and_arguments_string);
FunctionNameAndArguments function_name_and_arguments;
// function name
ss >> function_name_and_arguments.function_name;
// arguments
auto& arguments = function_name_and_arguments.arguments;
while (!ss.eof()) {
string function_type;
ss >> function_type;
// integer
if (function_type == "int") {
int value;
ss >> value;
arguments.push_back(value);
}
// string
else if (function_type == "string") {
string value;
ss >> value;
arguments.push_back(value);
}
else {
throw exception("unknown argument type");
}
}
return function_name_and_arguments;
}
int main() {
unordered_map<string, RPCHandler> functions = {
{ "greetWorld", make_invoker(&greetWorld) },
{ "greetName", make_invoker(&greetName) },
{ "printAddition", make_invoker(&printAddition) }
};
char server;
cout << "Server? (y/n): " << endl;
cin >> server;
// server
if (server == 'y') {
// accept client
TcpListener listen;
listen.listen(25565);
TcpSocket client;
listen.accept(client);
size_t received;
// receive size of string
size_t size;
client.receive(&size, sizeof(size), received);
// receive function name and arguments as string
string function_name_and_arguments_string;
function_name_and_arguments_string.resize(size);
client.receive(
function_name_and_arguments_string.data(),
size,
received
);
// go through each line
istringstream lines(function_name_and_arguments_string);
string line;
while (getline(lines, line)) {
// parse function name and arguments
auto [function_name, arguments] = parseFunctionNameAndArguments(
line
);
// call function
functions.at(function_name)(
arguments
);
}
}
// client
else {
// connect to server
TcpSocket server;
server.connect("localhost", 25565);
// function calls string
const string function_calls =
"greetWorld\n"
"greetName string Foo\n"
"printAddition int 1 int 2";
size_t size = function_calls.size();
// send size of string
server.send(&size, sizeof(size));
// send function calls string
server.send(function_calls.data(), size);
}
}
Let us assume you have a list of types (taking int and string as an example) usable in RPC, we can combine them in a RPC type and associated RPCHandler as follows:
using RPC = std::variant<int, std::string>;
using RPCHandler = std::function<void(std::vector<RPC>)>;
You want to create a std::map<std::string, RPCHandler> dispatch so you can do (given a std::vector<RPC> args):
dispatch[command](args);
This map can be constructed as follows:
void test0();
void test2(int, std::string);
std::map<std::string, RPCHandler> dispatch = {
{ "test0", make_invoker(test0) },
{ "test2", make_invoker(test2) },
};
where make_invoker returns a lambda of the correct shape.
The body of this lambda passes the function pointer, argument vector, and a std::index_sequence to invoke_rpc:
template<class... Arg>
RPCHandler make_invoker(void (*f)(Arg...)) {
return [f](std::vector<RPC> args) {
invoke_rpc(f, args, std::index_sequence_for <Arg...>{});
};
}
Finally, invoke_rpc uses std::get on each argument in turn to convert it into the expected type. It does this by expanding the two given template parameter packs in parallel. Intuitively, this expands to f(std::get<Arg0>(args.at(0), std::get<Arg1>(args.at(1)) with as many arguments to f as it expects (since the index sequence has the same length Args...).
template<class... Arg, std::size_t... I>
void invoke_rpc(void (*f)(Arg...), std::vector<RPC> args, std::index_sequence<I...>) {
f(std::get<Arg>(args.at(I))...);
}
If the vector is too short you get a std::out_of_range error, if there is an argument mismatch you get a std::bad_variant_access. You can improve error handling by checking the size of args before calling f, and using std::holds_alternative to see if all passed values match their proscribed type.

Reading a WORD variable from a CArchive and casting to int at the same time

This might sound basic but:
WORD wSong = 0;
CArchive ar;
...
ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);
At the moment I read the WORD into a specific variable and the cast it.
Can I read it in and cast at the same time?
Please note I can't serialize a int. It must be a WORD and cast to int.
Or
ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);
I don't think there is a direct way. You could implement a helper function:
template <typename T, typename U>
T readAndCast (CArchive& ar) {
U x;
ar >> x;
return static_cast<T> (x);
}
m_sPublicTalkInfo.iSongStart = readAndCast<int, WORD>(ar);
It might be better to use the fixed-width integer types in your program, i.e. perhaps int_least16_t instead of int to be sure the type has the right size. WORD is fixed to 16bit, but int isn't. Also, WORD is unsigned and int isn't, so there could be an overflow during casting.
This is a example of how you could create a wrapper if you want the serialize syntax to remain consistent. It's designed to work with integrals and MFC unsigned types only.
#include <iostream>
#include <cstdint>
#include <sstream>
#include <type_traits>
// Fake the MFC types
using BYTE = std::uint8_t;
using WORD = std::uint16_t;
using DWORD = std::uint32_t;
using QWORD = std::uint64_t;
template<typename T>
struct is_valid_save_type : std::bool_constant<
std::is_same_v<BYTE, T> ||
std::is_same_v<WORD, T> ||
std::is_same_v<DWORD, T> ||
std::is_same_v<QWORD, T>
> {};
template<typename T>
struct is_valid_load_type : is_valid_save_type<T> {};
// Saves type T as a SaveType
template<typename T, typename SaveType>
struct save_as_type
{
explicit save_as_type(T value) : value(value) {}
explicit operator SaveType() const
{
return static_cast<SaveType>(value);
}
private:
T value;
// This class only works with integrals
static_assert(std::is_integral_v<T>);
// SaveType should be BYTE/WORD/DWORD/QWORD only
static_assert(is_valid_save_type<SaveType>::value);
};
// Loads type T as a LoadType
template<typename T, typename LoadType>
struct load_as_type
{
explicit load_as_type(T& value) : value_(value) {}
load_as_type& operator=(LoadType rhs)
{
value_ = rhs;
return *this;
}
private:
T& value_;
// T should be an integral
static_assert(std::is_integral_v<T>);
// T must be non-constant
static_assert(!std::is_const_v<T>);
// LoadType should be BYTE/WORD/DWORD/QWORD only
static_assert(is_valid_load_type<LoadType>::value);
};
class CArchive;
// Make the above types serializable
template<typename T, typename SaveType>
CArchive& operator<<(CArchive& ar, save_as_type<T, SaveType> const& s)
{
ar << static_cast<SaveType>(s);
}
template<typename T, typename LoadType>
CArchive& operator>>(CArchive& ar, load_as_type<T, LoadType> l)
{
LoadType t{};
ar >> t;
l = t;
}
// Use the following two functions in your code
template<typename SaveType, typename T>
save_as_type<T, SaveType> save_as(T const& t)
{
return save_as_type<T, SaveType>{ t };
}
template<typename LoadType, typename T>
load_as_type<T, LoadType> load_as(T& t)
{
return load_as_type<T, LoadType>{ t };
}
// Prevent loading into temporaries; i.e. load_as<BYTE>(11);
template<typename T, typename LoadType>
load_as_type<T, LoadType> load_as(const T&& t) = delete;
// Fake MFC Archive
class CArchive
{
public:
CArchive& operator<<(int i)
{
std::cout << "Saving " << i << " as an int\n";
return *this;
}
CArchive& operator<<(BYTE b)
{
std::cout << "Saving " << (int)b << " as a BYTE\n";
return *this;
}
CArchive& operator<<(WORD w)
{
std::cout << "Saving " << (int)w << " as a WORD\n";
return *this;
}
CArchive& operator<<(DWORD d)
{
std::cout << "Saving " << (int)d << " as a DWORD\n";
return *this;
}
CArchive& operator>>(int& i)
{
std::cout << "Loading as an int\n";
return *this;
}
CArchive& operator>>(BYTE& b)
{
std::cout << "Loading as a BYTE\n";
return *this;
}
CArchive& operator>>(WORD& w)
{
std::cout << "Loading as a WORD\n";
return *this;
}
CArchive& operator>>(DWORD& d)
{
std::cout << "Loading as a DWORD\n";
return *this;
}
};
int main()
{
CArchive ar;
int out_1 = 1;
int out_2 = 2;
int out_3 = 3;
int out_4 = 4;
ar << out_1 <<
save_as<BYTE>(out_2) <<
save_as<WORD>(out_3) <<
save_as<DWORD>(out_4);
std::cout << "\n";
int in_1 = 0;
int in_2 = 0;
int in_3 = 0;
int in_4 = 0;
ar >> in_1 >>
load_as<BYTE>(in_2) >>
load_as<WORD>(in_3) >>
load_as<DWORD>(in_4);
return 0;
}
Output:
Saving 1 as an int
Saving 2 as a BYTE
Saving 3 as a WORD
Saving 4 as a DWORD
Loading as an int
Loading as a BYTE
Loading as a WORD
Loading as a DWORD

variadic templates with template function names and passing arguments and return values around

following from this question, I have been trying to create a template function that calls all same-named methods of its mixins. This has been done and verified in the previous question.
Now I am attempting to get the return value of SensorType::
Analytically:
#include<iostream>
#include <string>
struct EdgeSensor
{
void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; }
void updat2(const int i ) { std::cout << "EdgeSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl;
return std::string("EdgeSensor::printStats"); }
};
struct TrendSensor
{
void update(int i ) { std::cout << "TrendSensor::update" << i << std::endl; }
void updat2(const int i ) { std::cout << "TrendSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl;
return std::string("TrendSensor::printStats"); }
};
template <class T, void (T::*)(const int)>
struct Method { };
template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
template <class T, void(T::*M)(const int)>
int runSingle(Method<T, M> , const int i) {
(this->*M)(i);
return 0;
}
template <class... Ts>
void runAll(const int i) {
int run[sizeof...(Ts)] = { runSingle(Ts{},i)... };
(void)run;
}
public:
void update() {
runAll<Method<SensorType, &SensorType::update>...>(2);
}
void updat2() {
const int k = 3;
runAll<Method<SensorType, &SensorType::updat2>...>(k);
}
void printStats() {
// runAll<Method<SensorType, &SensorType::printStats>...>();
}
};
int main() {
{
BaseSensor<EdgeSensor,TrendSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
{
BaseSensor<EdgeSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
}
The above compiles and runs fine. The problem is: how can I gather the return values (std::strings) from running the mixin SensorType::printStats() methods in BaseSensor::printStats() ?
If I try to create a 2ndary version of the run* functions and the Method template, I fail to make it compile. Say I did:
template <class T, void (T::*)()>
struct Method2 { };
template <class T, void(T::*M)()>
int runSingle2(Method2<T, M>) {
(this->*M)();
return 0;
}
template <class... Ts>
void runAll2() {
std::string s;
int run[sizeof...(Ts)] = { s = runSingle2(Ts{})... };
(void)run;
std::cout << "s=" << s << std::endl;
}
public:
void update() {
int k = 4;
runAll<Method<SensorType, &SensorType::update>...>(k);
}
void printStats() {
runAll2<Method2<SensorType, &SensorType::printStats>...>();
}
};
This does not compile saying
g++ -Wall -Wextra -g -std=c++11 -c -o "obj_dbg/main.opp" "main.cpp"
main.cpp: In instantiation of ‘void BaseSensor<SensorType>::printStats() [with SensorType = EdgeSensor, TrendSensor]’:
main.cpp:65:20: required from here
main.cpp:58:8: error: could not convert template argument ‘&EdgeSensor::printStats’ to ‘void (EdgeSensor::*)()’
make: *** [obj_dbg/main.opp] Error 1
So HOW can I grab the return values from SensorType::printStats()?
Not sure if you can use c++11, if so, then I think this is the easiest?
#include <iostream>
#include <string>
struct EdgeSensor
{
void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; }
void updat2(const int i ) { std::cout << "EdgeSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl;
return std::string("EdgeSensor::printStats"); }
};
struct TrendSensor
{
void update(int i ) { std::cout << "TrendSensor::update" << i << std::endl; }
void updat2(const int i ) { std::cout << "TrendSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl;
return std::string("TrendSensor::printStats"); }
};
template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
public:
void update() {
auto v = { (static_cast<SensorType*>(this)->update(1), 0)... }; // *
(void) v;
}
void updat2() {
const int k = 3;
auto v = { (static_cast<SensorType*>(this)->updat2(k), 0)... }; // *
(void) v;
}
void printStats() {
auto v = { static_cast<SensorType*>(this)->printStats()... };
for (auto s : v) {
std::cout << s << std::endl;
}
}
};
int main() {
{
BaseSensor<EdgeSensor,TrendSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
{
BaseSensor<EdgeSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
}
NOTE: I am using a gcc extension here, but I think you are using gcc, so it should be okay
Here is you code reviewed so as it works as requested:
#include<iostream>
#include <string>
#include <vector>
struct EdgeSensor
{
void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; }
void updat2(const int i ) { std::cout << "EdgeSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl;
return std::string("EdgeSensor::printStats"); }
};
struct TrendSensor
{
void update(int i ) { std::cout << "TrendSensor::update" << i << std::endl; }
void updat2(const int i ) { std::cout << "TrendSensor::updat2" << i << std::endl; }
std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl;
return std::string("TrendSensor::printStats"); }
};
template<typename ... SensorType>
class BaseSensor : public SensorType ... {
template<typename F>
struct Invoke;
template<typename R, typename... A>
struct Invoke<R(A...)> {
template <R(SensorType::* ...M)(A...), typename T>
static std::vector<R> run(T *t, A... args) {
std::vector<R> vec;
int arr[] = { (vec.push_back((t->*M)(args...)), 0)... };
(void)arr;
return vec;
}
};
template<typename... A>
struct Invoke<void(A...)> {
template <void(SensorType::* ...M)(A...), typename T>
static void run(T *t, A... args) {
int arr[] = { ((t->*M)(args...), 0)... };
(void)arr;
}
};
public:
void update() {
Invoke<void(int)>::template run<&SensorType::update...>(this, 2);
}
void updat2() {
const int k = 3;
Invoke<void(int)>::template run<&SensorType::updat2...>(this, k);
}
void printStats() {
auto vec = Invoke<std::string(void)>::template run<&SensorType::printStats...>(this);
for(auto &&v: vec) {
std::cout << "--->" << v << std::endl;
}
}
};
int main() {
{
BaseSensor<EdgeSensor,TrendSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
{
BaseSensor<EdgeSensor> ets;
ets.update();
ets.updat2();
ets.printStats();
}
}
I refactored a bit the code, for there was no need for the Method class. This works as intended and the strings returned by the printStats methods are now collected in a std::vector and returned to the caller.
To extend the solution to any type of member function you could do (and actually a bit simplify it still having in mind c++11 restriction). The approach resolves type of member function to be able to infer its result type. It also uses InferOwnerType to infer mixin type and avoid direct passing of statically casted this pointer. Depending on the result of the member function now we can store it into an array or use the trick with int array just to be sure each member function is invoked.
#include <iostream> // std::cout std::endl
#include <string> // std::string
#include <utility> // std::declval
struct EdgeSensor //a mixin
{
void update(int a){ std::cout << "EdgeSensor::update" << a << std::endl; }
std::string updat2(int const v) { return "EdgeSensor::printStats"; }
};
struct TrendSensor //another mixin
{
void update(int a){ std::cout << "TrendSensor::update" << std::endl; }
std::string updat2(int const v) { return "TrendSensor::printStats"; }
};
template <class Res, class This, class... Args>
This InferOwnerType(Res (This::*foo)(Args...)) { }
template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
template <class M, class... Args>
auto run(M m, Args... args)
-> decltype((std::declval<decltype(InferOwnerType(m))*>()->*m)(args...)) {
return (static_cast<decltype(InferOwnerType(m))*>(this)->*m)(args...);
}
public:
template <class... Args>
void update(Args... args) {
int arr[] = {(run(&SensorType::update, args...), 0)...};
(void)arr;
}
template <class... Args>
void updat2(Args... args) {
std::string s[] = {run(&SensorType::updat2, args...)...};
for (int i = 0; i < sizeof...(SensorType); i++)
std::cout << s[i] << std::endl;
}
};
int main() {
BaseSensor<EdgeSensor, TrendSensor> bs;
bs.update(4);
bs.updat2(0);
BaseSensor<EdgeSensor> bs2;
bs2.update(1);
bs2.updat2(0);
}

C++ std::function variable with varying arguments

In my callback system I want to store std::function (or something else) with varying arguments.
Example:
I want to call void()
I want to call void(int, int)
I want 1) and 2) to be stored in the same variable and choose what to call in actuall call
FunctionPointer f0;
FunctionPointer f2;
f0();
f2(4, 5);
Is it possible to do something like this? Or I have to create several "FuntionPointer" templates based on input arguments count.
EDIT
Is it possible to utilize std::bind somehow for this task? With std::bind I can have std::function<void()> f = std::bind(test, 2, 5);
EDIT 2
Practical use case: I have a trigger system and I want to assign funtion pointers to actions, so when action happen, function is called.
Pseudo-code sample:
structure Trigger
{
Function f;
}
Init:
Trigger0.f = pointer to some function ()
Trigger1.f = pointer to some function (a, b)
Input:
Find Trigger by input
if (in == A) Trigger.f();
else Trigger.f(10, 20)
or if possible
Input:
Find Trigger by input
if (in == A) f = bind(Trigger.f);
else f = bind(Trigger.f, 10, 20);
f()
std::function<void()> and std::function<void(int, int)> are two absolutely distinct types. You need some sort of union functionality (or polymorphism) to store an object of an unknown type.
If you can use Boost, you could easily do this with boost::variant:
// Declaration:
boost::variant<std::function<void()>, std::function<void(int, int)> > f;
// Calling, explicit:
if (fContainsNullary()) {
boost::get<std::function<void()>>(f)();
} else {
boost::get<std::function<void(int, int)>>(f)(4, 5);
}
It is up to you to provide the logic of fContainsNullary(). Alternatively, you can use the variant's own stored knowledge of value type by using a visitor:
struct MyVisitor : boost::static_visitor<void>
{
result_type operator() (const std::function<void()> &a) {
a();
}
result_type operator() (const std::function<void(int, int)> &a) {
a(4, 5);
}
};
// Calling, via visitor:
boost::apply_visitor(MyVisitor(), f);
If Boost is not an option, you can hand-craft a suitable union for much the same purpose.
The following solution might work for you (I'm not sure that the code is absolutely correct here):
Create a wrapper for std::function with virtual destructor to enable using dynamic cast
class function_wrapper_base
{
virtual ~function_wrapper_base();
}
template <class... Args>
class function_wrapper
: public function_wrapper_base
{
public:
std::function<void, Args...> f;
...
};
Then create a class variant_function_holder
class variant_function_holder
{
std::unique_ptr<function_wrapper_base> f;
...
template <class... Args>
void operator()(Args&&... args)
{
function_wrapper<std::decay<Args>::type...> * g = dynamic_cast<function_wrapper<std::decay<Args>::type...>>(f.get());
if (g == nullptr)
{
// ToDo
}
g->f(std::forward<Args>(args)...);
}
};
Well, if you can use RTTI, you can define a MultiFuncObject like this, and you can easily bind other functions. Also, you can easily call them. But unfortunately, this approach only works for a limited number of arguments. But actually boost::bind also supports limited number of arguments (by default 9). So you can extend this class to satisfy your needs.
Before giving you the source of MultiFuncObject, I want to show you how you can use it. It takes an template argument to be used as return type. You can bind new functions with += operator. With some template magic, the class distinguishes differences between bound functions with same count of arguments with at least one different argument type.
You need C++11, because MultiFuncObject uses std::unordered_map and std::type_index.
Here is usage:
#include <iostream>
using namespace std;
void _1() {
cout << "_1" << endl;
}
void _2(char x) {
cout << "_2" << " " << x << endl;
}
void _3(int x) {
cout << "_3" << " " << x << endl;
}
void _4(double x) {
cout << "_4" << " " << x << endl;
}
void _5(int a, int b) {
cout << "_5" << " " << a << " " << b << endl;
}
void _6(char a, int b) {
cout << "_6" << " " << a << " " << b << endl;
}
void _7(int a, int b, int c) {
cout << "_7" << " " << a << " " << b << " " << c << endl;
}
int main() {
MultiFuncObject<void> funcs;
funcs += &_1;
funcs += &_2;
funcs += &_3;
funcs += &_4;
funcs += &_5;
funcs += &_6;
funcs += &_7;
funcs();
funcs('a');
funcs(56);
funcs(5.5);
funcs(2, 5);
funcs('q', 6);
funcs(1, 2, 3);
return 0;
}
I hope this is close to what you want. Here is the source of MultiFuncObject:
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
using namespace std;
template <typename R>
class MultiFuncObject {
unordered_map<type_index, void (*)()> m_funcs;
public:
MultiFuncObject<R> operator +=( R (* f)() ) {
m_funcs[typeid( R() )] = (void (*)()) f;
return *this;
}
template <typename A1>
MultiFuncObject<R> operator +=( R (* f)(A1) ) {
m_funcs[typeid( R(A1) )] = (void (*)()) f;
return *this;
}
template <typename A1, typename A2>
MultiFuncObject<R> operator +=( R (* f)(A1, A2) ) {
m_funcs[typeid( R(A1, A2) )] = (void (*)()) f;
return *this;
}
template <typename A1, typename A2, typename A3>
MultiFuncObject<R> operator +=( R (* f)(A1, A2, A3) ) {
m_funcs[typeid( R(A1, A2, A3) )] = (void (*)()) f;
return *this;
}
R operator()() const
{
unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R() ));
if (it != m_funcs.end()) {
R (*f)() = ( R (*)() )(it->second);
(*f)();
}
}
template <typename A1>
R operator()(A1 a1) const
{
unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1) ));
if (it != m_funcs.end()) {
R (*f)(A1) = ( R (*)(A1) )(it->second);
(*f)(a1);
}
}
template <typename A1, typename A2>
R operator()(A1 a1, A2 a2) const
{
unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2) ));
if (it != m_funcs.end()) {
R (*f)(A1, A2) = ( R (*)(A1, A2) )(it->second);
(*f)(a1, a2);
}
}
template <typename A1, typename A2, typename A3>
R operator()(A1 a1, A2 a2, A3 a3) const
{
unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2, A3) ));
if (it != m_funcs.end()) {
R (*f)(A1, A2, A3) = ( R (*)(A1, A2, A3) )(it->second);
(*f)(a1, a2, a3);
}
}
};
It stores different function prototypes using std::unordered_map with keys of std::type_index and values of void (*)(). When needed, the correct function is retrieved using that map.
Here is the working example
C++11 to the rescue!
If you can generalize your function to a functor object taking no arguments, then you can call it with any lambda.
#include <iostream>
using namespace std;
template <class F>
void call_it(F&& f)
{
f();
}
int main()
{
int x = 50, y = 75;
call_it([] () { cout << "Hello!\n"; });
call_it([x,y] () { cout << x << " + " << y << " = " << x + y << '\n';});
return 0;
}
If std::function is not necessary for you, you can create a proxy class.
class fn_t {
public:
typedef void (*fn_1_t)();
typedef void (*fn_2_t)(int, int);
fn_1_t fn_1;
fn_2_t fn_2;
fn_t operator=(fn_1_t func_1) { fn_1 = func_1; return *this; }
fn_t operator=(fn_2_t func_2) { fn_2 = func_2; return *this; }
void operator()() { (*fn_1)(); }
void operator()(int a, int b) { (*fn_2)(a, b); }
};
#include <iostream>
using namespace std;
void first() {
cout << "first" << endl;
}
void second(int a, int b) {
cout << "second " << a << " : " << b << endl;
}
int main() {
fn_t f;
f = &first;
f = &second;
f();
f(5, 4);
return 0;
}
Class fn_t automatically works with two prototypes you want, assigns automatically needed one, and it can call functions with both prototypes by overlading () operator with appropriate parameters.
You may want to check for validity of function pointers fn_1 and fn_2 but I didn't include this checking for minimality.
The advantage of this is that you only need C++ and not even STL and Boost.
The other answers are fine but I want to show my solution as well.
It's a small header with which you can "elongate" function signatures.
This allows you to do this (extract from the github example):
int foo_1p(int a);
int foo_2p(int a, int b);
int foo_3p(int a, int b, int c);
int foo_4p(int a, int b, int c, int d);
int foo_5p(int a, int b, int c, int d, int e);
int foo_6p(int a, int b, int c, int d, int e, int f);
int foo_7p(int a, int b, int c, int d, int e, int f, std::string g);
...
int main()
{
std::unordered_map<std::string, std::function<int(int, int, int, int, int, int, std::string)>> map;
map["foo_1p"] = ex::bind(foo_1p, ph, ph, ph, ph, ph, ph);
map["foo_2p"] = ex::bind(foo_2p, ph, ph, ph, ph, ph);
map["foo_3p"] = ex::bind(foo_3p, ph, ph, ph, ph);
map["foo_4p"] = ex::bind(foo_4p, ph, ph, ph);
map["foo_5p"] = ex::bind(foo_5p, ph, ph);
map["foo_6p"] = ex::bind(foo_6p, ph);
map["foo_7p"] = foo_7p;
for (const auto& f : map)
{
std::cout << f.first << " = " << f.second(1, 1, 1, 1, 1, 1, "101") << std::endl;
}
}