c++11: call virtual base-class method using a central command-mapper - c++

I'd like to make a command Mapper that accepts commands of a certain type and hands them over to runtime-registered members of various sub-classes of a common Bindable class.
As the sub-class members are of different types, I struggle with programming a working Mapper class. How do I need to implement it to make it work?
#include <iostream> // std::cout
#include <functional> // std::bind
#include <map> // std::map
#include <vector> // std::vector
struct Command {
int cmdNum;
int numArgs;
std::vector<int> args;
};
struct Invocation {
enum Source {
SOURCE_X = 0, SOURCE_Y, SOURCE_Z,
SOURCE_END
};
Source src;
Command cmd;
};
struct Bindable {
virtual void handleCmd(Command Cmd) = 0;
};
struct A : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class A" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
struct B : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class B" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
The problematic Mapper:
struct Mapper {
void bindCmd(Command cmd, Bindable* mBindable) {
//Fill a multimap with cmd.cmdNum as keys and mBindable as values
}
//Send cmd to each registered Bindable for the respective cmdNum
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) {
mMatch.second()->handleCmd(inv.cmd);
}
}
private:
std::multimap<int, Bindable*> mBinds;
};
The desired usage shall be:
int main() {
A a;
B b;
Command cmdA = {200, 4, {1,2,3,4}};
Command cmdB = {400, 3, {3,2,1}};
Command cmdC = {600, 2, {8,9}};
Invocation invA = {Invocation::SOURCE_X, cmdA};
Invocation invB = {Invocation::SOURCE_Z, cmdB};
Invocation invC = {Invocation::SOURCE_Z, cmdC};
Mapper mMapper;
//Register Commands
mMapper.bindCmd(cmdA, &a);
mMapper.bindCmd(cmdB, &a);
mMapper.bindCmd(cmdA, &b);
mMapper.bindCmd(cmdC, &b);
//React to incoming Invocations
mMapper.handleInv(invA); //Call handleCmd of a and b
mMapper.handleInv(invB); //Call handleCmd of a
mMapper.handleInv(invC); //Call handleCmd of b
}

The code in the OP works, as far as I can see, when two minor bugs are fixed:
std::multimap<int, Bindable*> mBinds;
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) { // 1
mMatch.second()->handleCmd(inv.cmd); // 2
}
}
1
std::multimap<K,V>::equal_range returns a std::pair of iterators, where the member first specifies the begin, and the member second the end of an iterator-range.
The range-based for loop expects on the right-hand side of the : something that can provide the begin and end of an iterator-range, but searches for free functions or member functions with the names begin and end. Therefore, we have to translate std::pair::first -> begin() and std::pair::second -> end().
There are of course library solutions for this (e.g. boost). A minimal solution could be:
template<typename It>
struct iterator_pair_range
{
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<typename It>
auto make_iterator_pair_range(std::pair<It, It> const& p)
-> iterator_pair_range<It>
{ return {p.first, p.second}; }
for(auto mMatch : make_iterator_pair_range(mMatches)) {
2
mMatch.second()->handleCmd(inv.cmd); // 2
The member second of std::pair is a public data member, not a member function:
mMatch.second->handleCmd(inv.cmd); // 2
I'll suggest you post your code on CodeReview.SE, since there are more general, safer (e.g. lifetime issues) and possibly easier solutions to this general problem. For example, there is the boost.signals2 library; also there is the std::function wrapper that allows storing objects of arbitrary type, as long as they can be called with a certain signature.

Related

How to specialize a function using enum

I'm trying to refactor some code. Basically is a state machine based with enum.
There are a lot of switch statements and functions that got called with different names and ambiguations.
Since they force me to keep the enum, I would like to refactor it using template. Basically I would like to use template to implement polymorphism. Since the states are limited there should be a way but I cannot find the best one.
#include <iostream>
enum class AnimalType
{
Dog,
Cat
};
template<AnimalType T>
void Foo()
{
std::cout << "Unknown animal\n";
}
template<>
void Foo<AnimalType::Dog>()
{
std::cout << "I'm a dog\n";
}
template<>
void Foo<AnimalType::Cat>()
{
std::cout << "I'm a cat\n";
}
int main()
{
AnimalType CurrentAnimal = AnimalType::Dog;
// Foo<CurrentAnimal>(); Won't compile
return 0;
}
You need a compile time evaluatable constant, this will work
int main()
{
constexpr auto CurrentAnimal = AnimalType::Dog;
Foo<CurrentAnimal>();
return 0;
}
or directly use
Foo<AnimalType::Dog>();
Note : you can't use your construct to make decissions at runtime.
Templates only lead to compile time polymorphism
As mentioned by #P Kramer's answer:
Note : you can't use your construct to make decissions at runtime. Templates only lead to compile time polymorphism.
You can't do that, but you can use the Compile-Time Dispatch and runtime parameter by passing the desired value as parameter while they are separated by Function Template Specialization. For example turn your enumerations value into actual types:
struct animal_t
{
std::string const name;
explicit animal_t(std::string const& name_)
: name(name_)
{
}
auto operator()() const
{
return name;
}
};
struct dog_t final : animal_t
{
using animal_t::animal_t;
};
struct cat_t final : animal_t
{
using animal_t::animal_t;
};
They you are able to specialize the function template:
/*!
*
* Other Programmer(s) interface
*
*/
template<typename Animal>
auto function(Animal const&)
{
assert(false);
}
/*!
*
* Implementation
*
*/
template<>
auto function(cat_t const& animal)
{
return animal();
}
template<>
auto function(dog_t const& animal)
{
return animal();
}
Now user (other programmer) of your library could easily interact with it for example by a GUI library:
QObject::connect(button1, &QPushButton::clicked, &application, [] {
cat_t cat("Some Cat");
auto const message = QString::fromStdString(function(cat));
QMessageBox::information(nullptr, " ", message);
});
QObject::connect(button2, &QPushButton::clicked, &application, [] {
dog_t dog("Some Dog");
auto const message = QString::fromStdString(function(dog));
QMessageBox::information(nullptr, " ", message);
});
Result: just for copy/past: runtime_dispatch_v1

How can I pass a member function with an unknown prototype to a class in C++?

I need to make a class (we'll call it Command) that takes in a string, processes it into function arguments, and then passes it to a member function of a different class. For my use, the member function that I pass to Command could come from a number of classes, and could have many different prototypes. I can guarantee that that member function will return void. Here's the code I imagine:
class Command {
public:
vector<tuple<int, string, any>> argument_specification;
SomeType callable;
Command(vector<tuple<int, string, any>> argument_spec, SomeType callable) {
this->argument_specification = argument_spec;
this->callable = callable;
}
void apply(string args) {
/* processing args according to this->argument_specification
to make a std::tuple arguments */
std::apply(this->callable, arguments);
}
};
class Action {
public:
print_two_arguments(int arg1, int arg2) {
std::cout << arg1 << ", " << arg2 << std::endl;
}
print_one_arguments(std::string arg1) {
std::cout << arg1 << std::endl);
}
}
int main() {
Action *actor = new Action();
// my argument specification code splits by string and then extracts
// arguments by position or keyword and replacing with a default if
// not specified
Command *command1 = new Command({{0, "first_arg", "something"}},
&actor->print_one_argument);
command1->apply("hello_world"); // Should print "hello_world"
Command *command2 = new Command({{0, "first_arg", 2},
{1, "second_arg", 10}},
&actor->print_two_arguments);
command2->apply("0 2"); // should print "0 2"
}
I don't really mind what method gets there - I've tried std::bind and can't quite get that to work, I've also tried lambdas. I'm currently trying a template class with a type deduced factory method. I'm also open to a macro definition that will fix this at compile time.
A couple ideas come to mind, but the key thing that I'm seeing is that you want to be able to take an arbitrary void function and call it with a single string. Templates can be really helpful here because you can use them to auto-deduce things such as how to build the tuple that you apply to the function.
This will be a semi-complicated meta-program-y solution, but I love that stuff; so I'm going to build a prototype. Also beware, this is the kind of solution that will result in absolutely horrendous compiler errors if you try to use it wrong.
My suggestion would be to make Command a templated type, where the command itself is templated on the parameter types of the function you want to pass it. If you need to be able to make a list of these to apply arguments to, then you can have a base class which provides the apply function. Since I don't fully understand how the argument specification is supposed to work, I'm punting on that and supporting keyword arguments only; but the way I built this, it should be fairly straightfoward to sub in your own argument splitter. I think. It could be cleaner, but I need to get back to my job.
Play with it on Compiler Explorer: https://godbolt.org/z/qqrn9bs1T
#include <any>
#include <functional>
#include <initializer_list>
#include <iostream>
#include <iterator>
#include <memory>
#include <regex>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>
using namespace std;
// Converts the string arguments to the actual types
template <class T> T convert_arg(std::string);
template <> std::string convert_arg<std::string>(std::string s) { return s; }
template <> int convert_arg<int>(std::string s) { return std::stoi(s); }
// Split on spaces
std::vector<string> tokenize(std::string s) {
istringstream iss(s);
return {istream_iterator<string>{iss}, istream_iterator<string>{}};
}
// Argument spec defines how to parse the arguments from the input. It
// contains the positional index in the string, the name of it, and a
// default value. It's effectively a mapping from the string being applied
// to the function being called.
//
// This could maybe be turned into a std::tuple<std::tuple<...>>, but
// I'm not sure. That could get a little messy with trying to iterate
// through it to build the argument list, and I don't think it buys us
// anything.
//
// For example, given the argument spec
// {{1, "first_arg", 0}, {0, "second_arg", "some_default"}}
// You could call a function that has the signature
// void (int, string);
// And you could parse the following argument strings (assuming space-delimited)
// "second_arg=hello first_arg=0"
// "words 1"
// "first_arg=5 more_text"
using argument_spec_t = std::vector<tuple<std::size_t, string, std::string>>;
class CommandBase {
public:
virtual void apply(string args) = 0;
};
// Concrete commands are templated on the argument types of the function
// that they will invoke. For best results, use make_command() to deduce
// this template from the function that you want to pass the Command in
// order to get references and forwarding correct.
template <class... ArgTs> class Command : public CommandBase {
public:
using callable_t = std::function<void(ArgTs...)>;
// Holds the argument specification given during constuction; this
// indicates how to parse the string arguments
argument_spec_t m_argument_specification;
// A function which can be invoked
callable_t m_callable;
Command(argument_spec_t argument_spec, callable_t callable)
: m_argument_specification(std::move(argument_spec)),
m_callable(std::move(callable)) {}
void apply(string args) {
//std::cout << "Apply " << args << std::endl;
std::tuple parsed_args =
build_args(split_args(std::move(args), m_argument_specification),
std::index_sequence_for<ArgTs...>{});
std::apply(m_callable, parsed_args);
}
private:
// Pre-processes the command arguments string into a
// std::unordered_map<size_t, std::string> where x[i] returns the text of the
// i'th argument to be passed to the function.
//
// \todo Support positional arguments
// \todo Be more robust
static std::unordered_map<size_t, std::string>
split_args(std::string args, const argument_spec_t &arg_spec) {
std::unordered_map<std::string, std::string> kw_args;
std::unordered_map<size_t, std::string> arg_map;
vector<string> tokens = tokenize(args);
for (const auto &token : tokens) {
auto delim = token.find("=");
auto key = token.substr(0, delim);
auto val = token.substr(delim + 1);
kw_args[key] = val;
// std::cout << "key = " << val << std::endl;
}
for (size_t i = 0; i < arg_spec.size(); ++i) {
const auto &[pos_index, key, default_val] = arg_spec[i];
auto given_arg_it = kw_args.find(key);
if (given_arg_it != kw_args.end())
arg_map[i] = given_arg_it->second;
else
arg_map[i] = default_val;
// std::cout << i << " -> " << arg_map[i] << std::endl;
}
return arg_map;
}
// Copies the arguments from the map returned by pre_process_args into a
// std::tuple which can be used with std::apply to call the internal function.
// This uses a faux fold operation because I'm not sure the right way to do a
// fold in more modern C++
// https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
template <std::size_t... Index>
std::tuple<ArgTs...>
build_args(std::unordered_map<size_t, std::string> arg_map,
std::index_sequence<Index...>) {
std::tuple<ArgTs...> args;
std::initializer_list<int> _{
(std::get<Index>(args) =
convert_arg<std::tuple_element_t<Index, std::tuple<ArgTs...>>>(
std::move(arg_map[Index])),
0)...};
return args;
}
};
// Factory function to make a command which calls a pointer-to-member
// function. It's important that the reference to the object stays in
// scope as long as the Command object returned!
template <class C, class... ArgTs>
std::unique_ptr<CommandBase> make_command(C &obj,
void (C::*member_function)(ArgTs...),
argument_spec_t argument_spec) {
return std::make_unique<Command<ArgTs...>>(
std::move(argument_spec), [&obj, member_function](ArgTs... args) {
(obj.*member_function)(std::forward<ArgTs>(args)...);
});
}
// Factory function to make a command which calls a std::function.
template <class... ArgTs>
std::unique_ptr<CommandBase>
make_command(std::function<void(ArgTs...)> callable,
argument_spec_t argument_spec) {
return std::make_unique<Command<ArgTs...>>(std::move(argument_spec),
std::move(callable));
}
// Factory function to make a command which calls a free function
template <class... ArgTs>
std::unique_ptr<CommandBase> make_command(void (*fn)(ArgTs...),
argument_spec_t argument_spec) {
return make_command(std::function<void(ArgTs...)>{fn},
std::move(argument_spec));
}
class Action {
public:
void print_two_arguments(int arg1, int arg2) {
std::cout << arg1 << ", " << arg2 << std::endl;
}
void print_one_argument(std::string arg1) { std::cout << arg1 << std::endl; }
};
void print_one_argument_free(std::string arg1) {
std::cout << arg1 << std::endl;
}
int main() {
Action actor;
// my argument specification code splits by string and then extracts
// arguments by position or keyword and replacing with a default if
// not specified
auto command1 = make_command(actor, &Action::print_one_argument,
argument_spec_t{{0, "first_arg", "something"}});
command1->apply("first_arg=hello_world"); // Should print "hello_world"
auto command2 = make_command(
actor, &Action::print_two_arguments,
argument_spec_t{{0, "first_arg", "2"}, {1, "second_arg", "10"}});
command2->apply("0 second_arg=2"); // should print "0 2"*/
auto command3 = make_command(&print_one_argument_free,
argument_spec_t{{0, "first_arg", "something"}});
command3->apply("first_arg=hello_again");
}
I think there are a number of ways to handle this problem, including function pointers with variable arguments, etc. But your fundamental problem is that you're asking one class to understand the internals of another class, which never works out well. I'd argue instead that you should have a parent Actor class that has a function that can be overridden by sub-classes and just passing an instance of the subclass instead. Each subclass may need to take an array of arguments, or even another container type that each subclass knows what it needs from within.
#include <iostream>
using namespace std;
class Data {
public:
std::string strdata;
int intinfo1;
int intinfo2;
};
class ActionBase {
public:
virtual void act(Data d) = 0;
};
class PrintIntinfos : public ActionBase {
public:
virtual void act(Data d) {
std::cout << d.intinfo1 << ", " << d.intinfo2 << std::endl;
}
};
class PrintStrData : public ActionBase {
public:
virtual void act(Data d) {
std::cout << d.strdata << std::endl;
}
};
int main()
{
ActionBase *Action1 = new PrintIntinfos();
Data d = Data();
d.intinfo1 = 42;
d.intinfo2 = -42;
Action1->act(d);
delete Action1;
d.strdata = "hello world";
Action1 = new PrintStrData();
Action1->act(d);
}
What you should actually do requires analysis of what your goals are with respect to base-pointers and containers and your data structure, flow, etc.
In your apply you describe something that really wants the context of the constructor. What if Command was
class Command {
std::function<void(std::string)> callable;
public:
template <typename... Args>
Command(std::function<std::tuple<Args...>(std::string)> argument_spec, std::function<void(Args...)> callable)
: callable([=](std::string args) { std::apply(callable, argument_spec(args)); })
{ }
void apply(std::string args) {
callable(args);
}
};
You would still be able to use your argument specification code to create the argument_spec parameter

creating type vector in c++

I have several classes that each of them has an ID and the Id is passed to the class as a template parameter:
typedef class1<1> baseClass;
typedef class2<2> baseClass;
typedef class<100> baseClass;
Now I need a map so if I can associate 1 with Class1 and 2 with Class2 and so on.
How can I create such vector? I am working on a header only library, so it should be a header only definition.
I am looking something that do the same thing that this code would do (if someone can compile it!):
std::map<int,Type> getMap()
{
std::map<int,Type> output;
output.add(1,class1);
output.add(2,class2);
output.add(100,class100);
}
The idea is that when I get as input 1, I create a class1 and when I receive 2, I create class2.
Any suggestion is very appreciated.
using this data, then I can write a function like this:
void consume(class1 c)
{
// do something interesting with c
}
void consume(class2 c)
{
// do something interesting with c
}
void consume(class3 c)
{
// do something interesting with c
}
void consume(int id,void * buffer)
{
auto map=getMap();
auto data= new map[id](buffer); // assuming that this line create a class based on map, so the map provide the type that it should be created and then this line create that class and pass buffer to it.
consume(data);
}
As a sketch:
class BaseClass { virtual ~BaseClass() = default; };
template<std::size_t I>
class SubClass : public BaseClass {};
namespace detail {
template<std::size_t I>
std::unique_ptr<BaseClass> makeSubClass() { return { new SubClass<I> }; }
template<std::size_t... Is>
std::vector<std::unique_ptr<BaseClass>(*)> makeFactory(std::index_sequence<Is...>)
{ return { makeSubclass<Is>... }; }
}
std::vector<std::unique_ptr<BaseClass>(*)> factory = detail::makeFactory(std::make_index_sequence<100>{});
We populate the vector by expanding a parameter pack, so we don't have to write out all 100 instantiations by hand. This gives you Subclass<0> at factory[0], Subclass<1> at factory[1], etc. up to Subclass<99> at factory[99].
If I understand correctly you want a map to create different types according to a given number.
If that is so, then the code should look something like this:
class Base
{
};
template <int number>
class Type : public Base
{
public:
Type()
{
std::cout << "type is " << number << std::endl;
}
};
using Type1 = Type<1>;
using Type2 = Type<2>;
using Type3 = Type<3>;
using CreateFunction = std::function<Base*()>;
std::map<int, CreateFunction> creators;
int main()
{
creators[1] = []() -> Base* { return new Type1(); };
creators[2] = []() -> Base* { return new Type2(); };
creators[3] = []() -> Base* { return new Type3(); };
std::vector<Base*> vector;
vector.push_back(creators[1]());
vector.push_back(creators[2]());
vector.push_back(creators[3]());
}
output:
type is 1
type is 2
type is 3
If you need only to create object, it would be enough to implement template creator function like:
template<int ID>
Base<ID> Create()
{
return Base<ID>();
}
And then use it:
auto obj1 = Create<1>();
auto obj2 = Create<2>();
// etc
Working example: https://ideone.com/urh7h6
Due to C++ being a statically-typed language, you may choose to either have arbitrary types that do a fixed set of things or have a fixed set of types do arbitrary things, but not both.
Such limitations is embodied by std::function and std::variant. std::function can have arbitrary types call operator() with a fixed signature, and std::variant can have arbitrary functions visit the fixed set of types.
Since you already said the types may be arbitrary, you may only have a fixed set of things you can do with such a type (e.g. consume). The simplest way is to delegate the hard work to std::function
struct Type
{
template<typename T>
Type(T&& t)
: f{[t = std::forward<T>(t)]() mutable { consume(t); }} {}
std::function<void()> f;
};
void consume(Type& t)
{
t.f();
}
What you are looking for is either the Stategy pattern:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class A {
public:
A() {}
virtual void doIt() {};
};
class Aa : public A {
public:
Aa() {}
virtual void doIt() {
std::cout << "do it the Aa way" << std::endl;
}
};
class Ab : public A {
public:
Ab() {}
virtual void doIt() {
std::cout << "do it the Ab way" << std::endl;
}
};
class Concrete {
public:
Concrete(std::string const& type) {
if (type == ("Aa")) {
_a.reset(new Aa());
} else if (type == "Ab") {
_a.reset(new Ab());
}
}
void doIt () const {
_a->doIt();
}
private:
std::unique_ptr<A> _a;
};
int main() {
std::vector<Concrete> vc;
vc.push_back(Concrete("Aa"));
vc.push_back(Concrete("Ab"));
for (auto const& i : vc) {
i.doIt();
}
return 0;
}
Will output:
do it the Aa way
do it the Ab way

pass userData from callback-begin to callback-end

How to appropriately cache userData that is generated from user's callbackBegin() and send it to user's callbackEnd().
Simple version (No userData - demo)
I want to create a complex database that support callback. For MCVE, let's say it is MyArray.
Here is a simple array class that supports callback but no userData.
#include <iostream>
template<class Derived>class MyArray{ //library - I design it.
public: void push_back(int s){
static_cast<Derived*>(this)->callbackBegin(s);
//do something about array
static_cast<Derived*>(this)->callbackEnd(s);
}
//other fields / functions
};
class Callback : public MyArray<Callback>{ //user's class
public: void callbackBegin(int s){
std::cout<<"callbackBegin"<<std::endl;
}
public: void callbackEnd(int s){
std::cout<<"callbackEnd"<<std::endl;
}
};
int main() {
Callback c;
c.push_back(5); //print: callbackBegin callbackEnd
return 0;
}
It works correctly.
The next step : I want to pass some userData from Callback::callbackBegin() to Callback::callbackEnd().
For example, userData is a clock time when Callback::callbackBegin() is called.
My poor solution (void*& userdata : demo)
Here is my attempt to implement it :-
#include <iostream>
#include <time.h>
template<class Derived>class MyArray{
public: void push_back(int s){
void* userData=nullptr; //#
static_cast<Derived*>(this)->callbackBegin(s,userData); //# ugly
//do something about array
static_cast<Derived*>(this)->callbackEnd(s,userData); //# ugly
}
};
class Callback : public MyArray<Callback>{
public: void callbackBegin(int s,void*& userData){ //#
userData=new clock_t(clock()); //# danger
std::cout<<"callbackBegin"<<std::endl;
}
public: void callbackEnd(int s,void*& userData){ //#
clock_t* userDataTyped=static_cast<clock_t*>(userData);
clock_t clock2=clock();
clock_t different=clock2 - (*userDataTyped);
std::cout<<"callbackEnd time(second)="
<<((float)different)/CLOCKS_PER_SEC<<std::endl;
delete userDataTyped; //# danger
}
};
int main() {
Callback c;
c.push_back(5); //print: callbackBegin callbackEnd time(second)=8.5e-05
return 0;
}
It also works correctly, but I believe it is a bad design (at various #) :-
new/delete in 2 places : potential memory leaking.
Strong pointer is preferred, but I don't know how to.
static_cast<clock_t*>(userData) is code-smell, at least for me.
(minor issue) an extra ugly parameter void*&
Question: What are design patterns / C++ magic to avoid such issues, while make MyArray concise, easy to use, maintainable (i.e. not much worse than the Simple version)?
Other notes:
In real cases, <5% of user's callback classes need userData.
Thus, I feel very reluctant to add void&* as an extra parameter.
Clarify: (edited) The minority cases usually need different types of userData e.g. Callback1 need clock_t, Callback2 need std::string, etc.
Proposed solution should restrain from using std::function<> or virtual function, because the performance is a major concern here.
Thank.
Pass data through a void pointer is a good C solution but (IMHO) not a C++ (specially: not a C++11/c++14/C++17, with auto and std::tuple) good one.
So I suggest to return a value from callbackBegin() and pass the value as first argument to `callbackEnd(); something like
auto r = static_cast<Derived*>(this)->callbackBegin(s);
static_cast<Derived*>(this)->callbackEnd(r, s);
Observe (C++11 and newer magic) that using auto as type of the value returned by callbackBegin(), you can return different types from different `callbackBegin().
Bonus suggestion: be more generic in MyArray::push_back(): using variadic templates, there is no need of fix the number and the types of arguments received by callbackBack() and callbackEnd().
Using variadic templates you can modify push_back() as follows
template <typename ... Args>
void push_back (Args const & ... args)
{
auto r = static_cast<Derived*>(this)->callbackBegin(args...);
static_cast<Derived*>(this)->callbackEnd(r, args...);
}
The following is a full working example with two different callback classes (with different number of arguments and different return types)
#include <tuple>
#include <iostream>
template <typename derT>
struct myA
{
template <typename ... Args>
void push_back (Args const & ... args)
{
auto r = static_cast<derT*>(this)->callbackBegin(args...);
static_cast<derT*>(this)->callbackEnd(r, args...);
}
};
struct cb1 : public myA<cb1>
{
int callbackBegin (int s)
{ std::cout << "cb1 b" << std::endl; return s+5; }
void callbackEnd (int r, int s)
{ std::cout << "cb1 e -" << r << ", " << s << std::endl; }
};
struct cb2 : public myA<cb2>
{
std::tuple<std::string, int> callbackBegin (std::string const & name,
int num)
{ std::cout << "cb2 b" << std::endl; return {name+";", num+1}; }
void callbackEnd (std::tuple<std::string, int> const &,
std::string const & name, int num)
{ std::cout << "cb2 e -" << name << ", " << num << std::endl; }
};
int main ()
{
cb1 c1;
c1.push_back(5);
cb2 c2;
c2.push_back("string arg", 7);
return 0;
}
std::any would allow you to hold clock_t (or any other) object and do away with the void* pointers, however that's a C++17 concept and not yet widely available (although there are implementations such as boost::any).
In the meantime, your code may benefit from a little composition over inheritance, as array and callback are conceptually pretty different and don't seem to belong in the same inheritance hierarchy. So, preferring composition, the code might look something like:
template<class T> struct ICallback
{
virtual void callbackBegin(int s, std::unique_ptr<T>& p) = 0;
virtual void callbackEnd(int s, std::unique_ptr<T>& p) = 0;
};
template<class T> class MyArray
{
public:
MyArray(std::shared_ptr<ICallback<T>> cb) { callback = cb; }
void push_back(int s)
{
callback->callbackBegin(s, usrDataPtr);
//do something about array
callback->callbackEnd(s, usrDataPtr);
}
protected:
std::shared_ptr<ICallback<T>> callback;
std::unique_ptr<T> usrDataPtr;
};
class ClockCallback : public ICallback<clock_t>
{
public:
void callbackBegin(int s, std::unique_ptr<clock_t>& c){
c = std::make_unique<clock_t>(clock());
std::cout << "callbackBegin" << std::endl;
}
void callbackEnd(int s, std::unique_ptr<clock_t>& c){
clock_t clock2 = clock();
clock_t different = clock2 - (*c);
std::cout << "callbackEnd time(second)="
<< ((float)different) / CLOCKS_PER_SEC << std::endl;
}
};
int main() {
std::shared_ptr<ClockCallback> c = std::make_shared<ClockCallback>();
MyArray<clock_t> ma(c);
ma.push_back(7);
return 0;
}
You can use a smart pointer to avoid manually deleting your userData
std::unique_ptr<clock_t> userData;
pass it as a reference to your callbacks
void callbackBegin(int s, std::unique_ptr<clock_t> &userData)
and initialize it this way
userData = std::make_unique<clock_t>(clock())
The C++ magic you're asking about is a known as a virtual method. Virtual method is one of the C++ native ways to implement the callback:
class MyArray{
public:
void push_back(int s) {
const auto userData = callbackBegin(s); //# beautiful
//do something about array
callbackEnd(s, userData); //# beautiful
}
private:
virtual clock_t callbackBegin(int) const = 0;
virtual void callbackEnd(int, const clock_t&) const = 0;
};
class Callback : public MyArray{
clock_t callbackBegin(int s) const final {
std::cout<<"callbackBegin"<<std::endl;
return clock(); //# safe
}
void callbackEnd(int s,const clock_t& userData) const final { //#
const auto different = clock() - userDataTyped;
std::cout << "callbackEnd time(second)=";
std::cout << different/CLOCKS_PER_SEC << std::endl;
//# safe
}
};
Another way is to pass two callable objects to the MyArray ctor and using those objects in the push_back method. The callable objects shall store calls to the relevant class Callback methods. Use std::function to implement those callable objects.

How to work around C++ pointer-to-member function limitation

C++ has limited ability to use pointer-to-member functions. I need something that will allow me to dynamically choose a callback member function, in order to use the Visitor pattern of the XMLNode::Accept(XMLVisitor *visitor) method from the TinyXML2 library.
To use XMLNode::Accept(), I must call it with a class which implements the XMLVisitor interface. Hence:
typedef bool (*Callback)(string, string);
class MyVisitor : public tinyxml2::XMLVisitor {
public:
bool VisitExit(const tinyxml2::XMLElement &e) {
callback(e.Name(), e.GetText());
}
Callback callback;
}
This works fine if my caller is NOT an object which wants to use one of its own methods as a callback function (so that it can access class variables). For example, this works:
bool myCallBackFunc(string e, string v) {
cout << "Element " << e << " has value " << v << endl;
return true;
}
int main(...) {
tinyxml2::XMLDocument doc;
doc.LoadFile("somefile.xml");
MyVisitor visit;
visit.callback = myCallBackFunc;
doc.Accept(&visit);
}
However, in my use case, the parsing is done inside a method in a class. I have multiple applications which have similar but unique such classes. I'd like to use only one generic MyVisitor class, rather than have the visitor class have unique knowledge of the internals of each class which will call it.
Thus, it would be convenient if the callback function were a method in each calling class so that I can affect the internal state of the object instantiated from that calling class.
Top level: I have 5 server applications which talk to 5 different trading partners, who all send XML responses, but each is enough different that each server app has a class which is unique to that trading partner. I'm trying to follow good OO and DRY design, and avoid extra classes having unique knowledge while still doing basically the same work.
Here's the class method I want Accept() to call back.
ServiceClass::changeState(string elem, string value) {
// Logic which sets member vars based on element found and its value.
}
Here's the class method which will call Accept() to walk the XML:
ServiceClass::processResponse(string xml) {
// Parse XML and do something only if certain elements present.
tinyxml2::XMLDocument doc;
doc.Parse(xml.c_str(), xml.length());
MyVisitor visit;
visit.callback = &changeState; // ERROR. Does not work.
visit.callback = &ServiceClass::changeState; // ERROR. Does not work.
doc.Accept(&visit);
}
What's a simple way to get what I want? I can imagine more classes with derived classes unique to each situation, but that seems extremely verbose and clumsy.
Note: In the interest of brevity, my sample code above has no error checking, no null checking and may even have minor errors (e.g. treating const char * as a string ;-).
Below is the std::bind(..) example for what you're trying to do in C++11. For earlier C++ versions you could use the boost::bind utilities.
Fix your MyVisitor::VisitExit(...) method to return a boolean, by the way.
The code is converting const char * to std::string. tinyxml2 does not guarantee that the char * arguments from Name() or GetText() are not null. In fact in my experience they will be null at some point. You should guard against this. For the sake of not modifying your example too much I've not protected against this possibility everywhere in the example.
typedef bool(*Callback)(string, string);
using namespace std;
class MyVisitor : public tinyxml2::XMLVisitor {
public:
bool VisitExit(const tinyxml2::XMLElement &e) {
// return callback(e.Name(), e.GetText());
return true;
}
Callback callback;
};
/** Typedef to hopefully save on confusing syntax later */
typedef std::function< bool(const char * element_name, const char * element_text) > visitor_fn;
class MyBoundVisitor : public tinyxml2::XMLVisitor {
public:
MyBoundVisitor(visitor_fn fn) : callback(fn) {}
bool VisitExit(const tinyxml2::XMLElement &e) {
return callback(e.Name() == nullptr ? "\0" : e.Name(), e.GetText() == nullptr ? "\0": e.GetText());
}
visitor_fn callback;
};
bool
myCallBackFunc(string e, string v) {
cout << "Element " << e << " has value " << v << endl;
return true;
}
int
main()
{
tinyxml2::XMLDocument doc;
doc.LoadFile("somefile.xml");
MyVisitor visit;
visit.callback = myCallBackFunc;
doc.Accept(&visit);
visitor_fn fn = myCallBackFunc; // copy your function pointer into the std::function<> type
MyBoundVisitor visit2(fn); // note: declare this outside the Accept(..) , do not use a temporary
doc.Accept(&visit2);
}
So from within the ServiceClass method you'd do:
ServiceClass::processResponse(string xml) {
// Parse XML and do something only if certain elements present.
tinyxml2::XMLDocument doc;
doc.Parse(xml.c_str(), xml.length());
// presuming changeState(const char *, const char *) here
visitor_fn fn = std::bind(&ServiceClass::changeState,this,std::placeholders::_1,std::placeholders::_2);
MyBoundVisitor visit2(fn); // the method pointer is in the fn argument, together with the instance (*this) it is a method for.
doc.Accept(&visit);
}
You can use generics in order to support whichever callback you'd like.
I've tried to mock the classes of the library in order to give you a fully runnable example:
#include <string>
#include <iostream>
#include <functional>
class XmlNode {
public:
XmlNode(const std::string& n, const std::string t) : name(n), txt(t) {}
const std::string& Name() const { return name; }
const std::string& GetText() const { return txt; }
private:
std::string name;
std::string txt;
};
class XMLVisitor {
public:
virtual void VisitExit(const XmlNode& node) = 0;
virtual ~XMLVisitor() {}
};
template<typename T>
class MyVisitor : XMLVisitor {
public:
MyVisitor() {}
void myInnerPrint(const XmlNode& node) {
std::cout << "MyVisitor::myInnerPrint" << std::endl;
std::cout << "node.Name(): " << node.Name() << std::endl;
std::cout << "node.GetText(): " << node.GetText() << std::endl;
}
void SetCallback(T newCallback) {
callback = newCallback;
}
virtual void VisitExit(const XmlNode& node) {
callback(node);
}
T callback;
};
int main() {
XmlNode node("In", "Member");
MyVisitor<std::function<void(const XmlNode&)>> myVisitor;
auto boundCall =
[&myVisitor](const XmlNode& node) -> void {
myVisitor.myInnerPrint(node);
};
myVisitor.SetCallback(boundCall);
myVisitor.VisitExit(node);
return 0;
}
First define a template and a helper function:
namespace detail {
template<typename F>
struct xml_visitor : tinyxml2::XMLVisitor {
xml_visitor(F&& f) : f_(std::move(f)) {}
virtual void VisitExit(const tinyxml2::XMLElement &e) {
f_(e);
}
private:
F f_;
};
}
template<class F>
auto make_xml_visitor(F&& f)
{
return detail::xml_visitor<std::decay_t<F>>(std::forward<F>(f));
}
Then use the helper function to construct a custom visitor from a lambda which captures this:
void ServiceClass::processResponse(std::string xml) {
// Parse XML and do something only if certain elements present.
tinyxml2::XMLDocument doc;
doc.Parse(xml.c_str(), xml.length());
auto visit = make_xml_visitor([this](const auto& elem)
{
this->changeState(elem.Name(), elem.GetText);
});
doc.Accept(std::addressof(visit));
}
The rule is that a function pointer must always accept a void * which is passed in to the module which calls it, and passed back. Or use a lambda which is the same thing with some of the machinery automated for you. (The void * is the "closure").
So
typedef bool (*Callback)(string, string, void *context);
class MyVisitor : public tinyxml2::XMLVisitor {
public:
bool VisitExit(const tinyxml2::XMLElement &e) {
callback(e.Name(), e.GetText(), contextptr);
}
Callback callback;
void *contextptr;
}
bool myCallBackFunc(string e, string v, void *context) {
ServiceClass *service = (ServiceClass *) context;
cout << "Element " << e << " has value " << v << endl;
service->ChangeState(e, v);
return true;
}