Function taking either reference to single element or a vector - c++

I want a function that modifies passed in elements of known type, matching against private data we iterate over in an outer loop comparing each to the passed in elements. Quantities are small so no need to build a map or optimize away the n² nature of this. In pseudo-code:
function fill_in_thing_data([in-out] things)
for (item in private_items)
info = wrangle(item)
for (thing in things)
if (matching(thing, info))
thing.data = info.data
Say private_items is expensive to iterator over, or to setup for iteration, and so I definitely want that in the outer loop.
Easy, right? Except that I want to two C++ overloaded functions that share some underlying code, one that takes a non-const reference to thing, one that takes a reference to a vector of things:
void fill_in_thing_data(Thing& single_thing);
void fill_in_thing_data(std::vector<Thing>& some_things);
What's the best way to share code between these 2 functions? I was thinking a helper function that takes iterators or some similar sequence type, and the first function passing in an iterator or sequence made out of the 1 element, the other one made out of the vector. I want to use C++ idioms so was looking at doing it with ForwardIterators.
Problem:
C++ iterators in general aren't easy. Ok using them is ok, but writing a function that takes any kind of ForwardIterator to simply a known type seems unexpectedly tricky.
Is a special case iterator over a single element reference something that's available? Or something that's easy to define? It seems the answers are no and no.
Instead of iterators, this is my ugly solution using a visitor lambda passed in, and a visit lambda passed back, allowing the iteration logic to be abstracted out of the shared function:
using VisitThing = std::function<bool(Thing& thing)>;
using ThingVisitor = std::function<bool(VisitThing visit)>;
void match_things(ThingVisitor visitor);
void fill_in_thing_data(Thing& single_thing) {
match_things([&](VisitThing visit) {
visit(single_thing);
});
}
void fill_in_thing_data(std::vector<Thing>& some_things) {
match_things([&](VisitThing visit) {
for (auto& thing : some_things) { visit(thing); }
});
}
void match_things(ThingVisitor visitor) {
auto stuff = fetch_private_stuff()
while (item = stuff.get_next_item()) { // can't change this home-brew iteration
auto item_info = wrangle(item) // but more complex, logic about skipping items etc
visitor([&](Thing& thing) {
if (item_info.token == thing.token)) {
thing.data = item_info.data;
}
}
}
}
Am I wrong and can do this with iterators without too much complexity? Or can I do this better in some other way, like maybe a good data structure class that can either be built with either the reference or the vector and then pass that in? Or like something else obvious I'm just not seeing? Thanks!

I hope I got OPs issue right. To me, it boiled down
to have a function which can be applied
to a single reference as well as
to a std::vector of instances.
There is actually a very simple solution which I even learnt (decades ago) in C but would work in C++ as well:
The function takes a pointer and a count:
void fill(Thing *pThing, size_t len);
To use it with a single instance:
Thing thing;
fill(&thing, 1);
To use it with a std::vector<Thing>:
std::vector<Thing> things;
fill(&things[0], things.size());
IMHO, this is somehow C-ish, beside of the fact, that OP mentioned iterators.
So, here we go:
template <typename ITER>
void fill(ITER first, ITER last)
{
for (const Item &item : items) {
for (ITER iter = first; iter != last; ++iter) {
if (matching(*iter, item)) iter->data = item;
}
}
}
// a wrapper for a single thing
void fill(Thing &thing) { fill(&thing, &thing + 1); }
// a wrapper for a vector of things
void fill(std::vector<Thing> &things) { fill(things.begin(), things.end()); }
The principle is still the same like above but using iterators.
A complete demo:
#include <iostream>
#include <vector>
// an item
struct Item {
int id = 0;
};
// the vector of OPs private items
std::vector<Item> items = {
{ 1 }, { 2 }, { 3 }
};
// a thing
struct Thing {
int id;
Item data;
Thing(int id): id(id) { }
};
// a hypothetical function matching a thing with an item
bool matching(const Thing &thing, const Item &item)
{
return thing.id == item.id;
}
// a generic fill (with matches) using iterators
template <typename ITER>
void fill(ITER first, ITER last)
{
for (const Item &item : items) {
for (ITER iter = first; iter != last; ++iter) {
if (matching(*iter, item)) iter->data = item;
}
}
}
// a wrapper for a single thing
void fill(Thing &thing) { fill(&thing, &thing + 1); }
// a wrapper for a vector of things
void fill(std::vector<Thing> &things) { fill(things.begin(), things.end()); }
// overloaded output operator for thing (demo sugar)
std::ostream& operator<<(std::ostream &out, const Thing &thing)
{
return out << "Thing { id: " << thing.id
<< ", data: Item { id: " << thing.data.id << "} }";
}
// demo sugar
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
// demonstrate
int main()
{
// call to fill a single instance of Thing
DEBUG(Thing thing(2));
DEBUG(std::cout << thing << '\n');
DEBUG(fill(thing));
DEBUG(std::cout << thing << '\n');
std::cout << '\n';
// call to fill a vector of Thing
DEBUG(std::vector<Thing> things = { Thing(2), Thing(3) });
DEBUG(for (const Thing &thing : things) std::cout << thing << '\n');
DEBUG(fill(things));
DEBUG(for (const Thing &thing : things) std::cout << thing << '\n');
}
Output:
Thing thing(2);
std::cout << thing << '\n';
Thing { id: 2, data: Item { id: 0} }
fill(thing);
std::cout << thing << '\n';
Thing { id: 2, data: Item { id: 2} }
std::vector<Thing> things = { Thing(2), Thing(3) };
for (const Thing &thing : things) std::cout << thing << '\n';
Thing { id: 2, data: Item { id: 0} }
Thing { id: 3, data: Item { id: 0} }
fill(things);
for (const Thing &thing : things) std::cout << thing << '\n';
Thing { id: 2, data: Item { id: 2} }
Thing { id: 3, data: Item { id: 3} }
Live demo on coliru
A note about the template function with iterators:
Just recently I became painfully aware that just naming something ITER is not enough to grant that it accepts iterators only. In my case, I had a variety of overloads, and the one accepting an iterator range was only one of them. So, I had to manage to eliminate ambiguities. For this, I found a quite simple solution (here in Stack Overflow) using SFINAE:
template <typename ITER,
typename = decltype(
*std::declval<ITER&>(), void(), // has dereference
++std::declval<ITER&>(), void())> // has prefix inc.
void fill(ITER first, ITER last);
An iterator is something which (among other things) has to provide a de-reference and an increment operator. The 2nd template type argument checks precisely this in its default initialization. (Template type arguments for SFINAE should never be used explicitly in template instances, of course.)
Live demo on coliru

Related

How emulate a templatised std::function in C++

Following is a basic instance of what I am doing in my C++ program. I have a list of listeners which are all std::functions. I have a concept DataType which means what kind of data the listener is interested in. The idea here is the same as publish-subscribe pattern. A method interested in certain kind of data should be able to add itself to the list of listeners using AddListener. Some methods are added & they receive a callback whenever required.
The program works fine !!
#include <iostream>
#include <functional>
#include <vector>
#include <string>
enum class DataType {
Type_1,
Type_2
// and so on
};
typedef std::function<void(std::pair<DataType, std::string>)> MyListenerType;
//template <typename T>
//typedef std::function<void(T>)> MyListenerType;
// How can I emulate the above so that a method passing any kind of primitive data-type namely "int, bool, float or double" can be added into
// my vector of listners.
std::vector<MyListenerType> my_data_listeners_1;
std::vector<MyListenerType> my_data_listeners_2;
void ListenerMethod_Instance_1(std::pair<DataType, std::string> information) {
DataType data_type = information.first;
std::string message = information.second;
std::cout << "ListenerMethod_Instance_1 called with message " << message << "\n";
}
void ListenerMethod_Instance_2(std::pair<DataType, std::string> information) {
DataType data_type = information.first;
std::string message = information.second;
std::cout << "ListenerMethod_Instance_2 called with message " << message << "\n";
}
void AddListener (MyListenerType listener, DataType type_of_interest) {
if (DataType::Type_1 == type_of_interest) {
my_data_listeners_1.push_back(listener);
std::cout << "Added a method instance for DataType::Type_1" << "\n";
}
else if (DataType::Type_2 == type_of_interest) {
my_data_listeners_2.push_back(listener);
std::cout << "Added a method instance for DataType::Type_2" << "\n";
}
else {
std::cout << "Listener type not supported" << "\n";
}
}
void CallAllListnersWhohaveSuscribed() {
if (!my_data_listeners_1.empty()) {
std::string send_message_1 = "some message 123";
std::pair <DataType, std::string> info_to_send_1 = std::make_pair (DataType::Type_1, send_message_1);
for(auto const &listener : my_data_listeners_1) {
listener(info_to_send_1);
}
}
if (!my_data_listeners_2.empty()) {
std::string send_message_2 = "some message 456";
std::pair <DataType, std::string> info_to_send_2 = std::make_pair (DataType::Type_2, send_message_2);
for(auto const &listener : my_data_listeners_2) {
listener(info_to_send_2);
}
}
}
int main() {
// Add ListenerMethod_Instance_1 for instance
DataType data_type_1 = DataType::Type_1;
auto listener_instance_1 = std::bind(ListenerMethod_Instance_1, std::placeholders::_1);
AddListener(listener_instance_1, data_type_1);
// Add ListenerMethod_Instance_2 for instance
DataType data_type_2 = DataType::Type_2;
auto listener_instance_2 = std::bind(ListenerMethod_Instance_2, std::placeholders::_1);
AddListener(listener_instance_2, data_type_2);
CallAllListnersWhohaveSuscribed();
return 0;
}
Following is the output of the program:
./stdFunctionTest
Added a method instance for DataType::Type_1
Added a method instance for DataType::Type_2
ListenerMethod_Instance_1 called with message some message 123
ListenerMethod_Instance_2 called with message some message 456
But here is how I want to modify & struggling with. The caveat is that every ListenerMethod_Instance_1 & ListenerMethod_Instance_2 have to parse the pair to get their info which I don't want to. I want to enable a method of any C++ primitive data type be it "int, bool, float or double" to be able to be added into the listeners vector & receive the callback. For example following method should be "add-able" into AddListener.
void ListenerMethod_Instance_3(int integer_data) {
std::cout << "ListenerMethod_Instance_3 called with integer_data " << integer_data << "\n";
}
Looking at this link here looks somewhat possible someway. But I'm struggling to adapt it to my use-case here. Please suggest.
So, basically how can I achieve templates functionality with std::functions ?
struct anything_view_t {
void* ptr=0;
template<class T, std::enable_if_t<!std::is_same<anything_view_t, std::decay_t<T>>{}, int> =0>
anything_view_t(T&&t):ptr(std::addressof(t)){}
anything_view_t()=default;
anything_view_t(anything_view_t const&)=default;
anything_view_t& operator=(anything_view_t const&)=default;
template<class T>
operator T() const { return *static_cast<T*>(ptr); }
};
this is a very unsafe type erasing view of anything.
struct any_callbacks {
std::unordered_map<std::type_index, std::vector<std::function<void(anything_view_t)>>> table;
template<class T>
void add_callback( std::function<void(T)> f ){
table[typeid(T)].push_back(f);
}
template<class T>
void invoke_callbacks(T t) const {
auto it = table.find(typeid(T));
if (it==table.end()) return;
for(auto&&f:it->second)
f(t);
}
};
something like the above should work. The type T must match exactly. References not supported. Code not compiled, design is sound, probably has typos.
This is not restructed to primitive types. You should pass T explicitly, don't rely on deduction as that is fragile.

Passing function and operator calls in object

I am wanting to make a class which allows me to lock an object from being modified. It would essentially be a template with a boolean specifying the lock state. Since it is a template, I won't know all the methods that can be called on the internal object, so I need a method to pass calls through...
template<class T>
class const_lock
{
public:
const_lock() : my_lock(false) {}
void set_const_lock(bool state) {my_lock = state;}
// HOW TO IMPLEMENT SOMETHING LIKE THESE????
//
template<typename...Args >
auto operatorANY_OPERATOR (Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_OPERATOR(args);
}
template<typename...Args >
auto operatorANY_CONST_OPERATOR (Args...args) const
{
return my_value.ANY_CONST_OPERATOR(args);
}
template<typename...Args >
auto ANY_METHOD(Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_METHOD(args);
}
template<typename...Args >
auto ANY_CONST_METHOD(Args...args) const
{
return my_value.ANY_CONST_METHOD(args);
}
private:
bool my_lock;
T my_value;
}
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
Any help would be appreciated. Thanks!
Edit: changed static assert to throw and exception
What you're trying to do looks rather difficult, but more importantly is over-complicated and unnecessary for what you're trying to do.
Essentially what you're trying to do (correct me if I'm wrong) is create a compile time check of whether you are supposed to able to modify an object at a given time. However, c++ already has a built in way of doing this. Simply declare or pass your object as const or const&, and the compiler will not allow you to modify non-mutable parts of the object. When you want to be able to modify it pass it without const. You can even cast it from const& to regular & when you want to go from code where you can't modify it directly to code where you can, though I don't recommend it.
edit: just saw a comment on the question about no reference arrays. Don't worry about that! The standard library has support for reference wrappers which allow you to essentially store references in arrays or anywhere else.
You can make a generic wrapper class that you can forward the function to using a lambda that captures a reference to the internal member. In this example I am just using an if statement to check if it is "locked" and if it is then we just modify a copy.
template<class T>
class const_lock
{
private:
bool my_lock;
mutable T my_value;
public:
const_lock() : my_lock(false) {}
void set_const_lock() { my_lock = true; }
template<typename F>
auto operator()(F f) const -> decltype(f(my_value))
{
if (my_lock)
{
T temp{my_value}; // make a copy
return f(temp);
}
else
return f(my_value); // modify wrraped value
}
};
int main()
{
const_lock<std::string> cl;
cl([](std::string& s) {
s = "foobar";
});
cl([](std::string& s) {
std::cout << s << std::endl;
});
cl.set_const_lock();
cl([](std::string& s) {
s = "we should still be foobar";
});
cl([](std::string& s) {
std::cout << s;
});
}
This is completely unimplementable. A trivial modification of your source code shows why this won't work.
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
if (rand() % 2)
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
You need to completely rethink your approach.
Below is an example illustrating what I would be trying to protect against
class Node
{
public:
Node(int id) : my_id(id) {}
// . . .
int id() {return my_id;}
private:
int my_id;
// . . .
};
class Grid
{
public:
Grid() {}
// . . .
void associate(Node* n) { my_nodes.push_back(n); }
private:
// . . .
std::vector<Node*> my_nodes;
};
Node* find(std::vector<Node>& Nodes, int ID)
{
for(auto i=Nodes.begin(); i!=Nodes.end(); ++i)
{
if (i->id() == ID)
{
return &*i;
}
}
}
main()
{
std::vector<Node> Nodes;
// fill Nodes with data
Grid Array;
Array.associate( find(Nodes,14325) );
Array.associate( find(Nodes,51384) );
Array.associate( find(Nodes,321684) );
// . . .
Nodes.push_back(Node(21616)); // this can invalidate my pointers in Array
}
If I was able to make my Nodes vairable be
const_lock<std::vector<Node>> Nodes;
then call
Nodes.set_const_lock(true);
after populating the data, I wouldn't need to worry about my pointers in Array getting messed up.

Creating a generic conversion function

I have a ResourceManager which takes in classes of type Resource. Resource is a parent class of other classes such as ShaderProgram, Texture, Mesh and even Camera who are completely unrelated to one another.
Suffice it to say, the ResourceManager works. But there is one thing that is very tedious and annoying, and that's when I retrieve the objects from the ResourceManager. Here is the problem:
In order to get an object from ResourceManager you call either of these functions:
static Resource* get(int id);
static Resource* get(const std::string &name);
The first function checks one std::unordered_map by an integer id; whereas the second function checks another std::unordered_map by the name that is manually given by the client. I have two versions of these functions for flexibility sakes because there are times where we don't care what the object contained within ResourceManager is (like Mesh) and there are times where we do care about what it is (like Camera or ShaderProgram) because we may want to retrieve the said objects by name rather than id.
Either way, both functions return a pointer to a Resource. When you call the function, it's as easy as something like:
rm::get("skyboxShader");
Where rm is just a typedef of ResourceManager since the class is static (all members/functions are static). The problem though is that the rm::get(..) function returns a Resource*, and not the child class that was added to the ResourceManager to begin with. So, in order to solve this problem I have to do a manual type conversion so that I can get ShaderProgram* instead of Resource*. I do it like this:
auto s = static_cast<ShaderProgram*>(rm::get(name));
So, everytime I want to access a Resource I have to insert the type I want to actually get into the static_cast. This is problematic insofar that everytime someone needs to access a Resource they have to type convert it. So, naturally I created a function, and being that ShaderProgram is the subject here, thus:
ShaderProgram* Renderer::program(const std::string &name)
{
auto s = static_cast<ShaderProgram*>(rm::get(name));
return s;
}
This function is static, and ResourceManager is a static class so the two go well hand-in-hand. This is a nice helper function and it works effectively and my program renders the result just fine. The problem is what I have to do when I'm dealing with other Resources; that means for every Resource that exists, there has to be a type-conversion function to accommodate it. Now THAT is annoying. Isn't there a way I can write a generic type-conversion function something like this?
auto Renderer::getResource(classTypeYouWant T, const std::string &name)
{
auto s = static_cast<T*>(rm::get(name));
return s;
}
Here, the auto keyword causes the function to derive which type it's supposed to be dealing with and return the result accordingly. My first guess is that I might have to use templates; but the problem with templates is that I can't limit which types get inserted into the function, and I really REALLY don't want floating-point id numbers, char ids, let alone custom-defined ids. It's either string (might change to const char* tbh) or ints or else.
How can I create a generic conversion function like the one described above?
Have you looked at using dynamic_cast? If the conversion fails with dynamic_cast the the pointer will be set to nullptr. So you could either write overloads for each type or you could write a template function where you pass the the type you want to convert to as well as the string or id and if the conversion succeeds or fails return true or false.
template<typename T>
bool Renderer::getResource(T*& type, const std::string &name)
{
type = dynamic_cast<decltype(std::remove_reference<decltype(T)>::type)>(rm::get(name));
if (type == nullptr)
return false;
return true;
}
OK, I did not like the idea of a typeless storage, but maybe you find that basic program as a start point. There are a lot of things which must be beautified, but some work must remain :-)
Again: It is a design failure to solve something in that way!
In addition to your example code this solution provides a minimum of safety while checking for the stored type while recall the element. But this solution needs rtti an this is not available on all platforms.
#include <map>
#include <iostream>
#include <typeinfo>
class ResourcePointerStorage
{
private:
std::map< const std::string, std::pair<void*, const std::type_info*>> storage;
public:
bool Get(const std::string& id, std::pair<void*, const std::type_info*>& ptr )
{
auto it= storage.find( id );
if ( it==storage.end() ) return false;
ptr= it->second;
return true;
}
bool Put( const std::string& id, void* ptr, const std::type_info* ti)
{
storage[id]=make_pair(ptr, ti);
}
};
template < typename T>
bool Get(ResourcePointerStorage& rm, const std::string& id, T** ptr)
{
std::pair<void*, const std::type_info*> p;
if ( rm.Get( id,p ))
{
if ( *p.second != typeid(T)) { return false; }
*ptr= static_cast<T*>(p.first);
return true;
}
else
{
return 0;
}
}
template < typename T>
void Put( ResourcePointerStorage& rm, const std::string& id, T *ptr)
{
rm.Put( id, ptr, &typeid(T) );
}
class Car
{
private:
int i;
public:
Car(int _i):i(_i){}
void Print() { std::cout << "A car " << i << std::endl; }
};
class Animal
{
private:
double d;
public:
Animal( double _d):d(_d) {}
void Show() { std::cout << "An animal " << d << std::endl; }
};
int main()
{
ResourcePointerStorage store;
Put( store, "A1", new Animal(1.1) );
Put( store, "A2", new Animal(2.2) );
Put( store, "C1", new Car(3) );
Animal *an;
Car *car;
if ( Get(store, "A1", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "A2", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "C1", &car)) { car->Print(); } else { std::cout << "Error" << std::endl; }
// not stored object
if ( Get(store, "XX", &an)) { } else { std::cout << "Expected false condition" << std::endl; }
// false type
if ( Get(store, "A1", &car)) { } else { std::cout << "Expected false condition" << std::endl; }
};
I've found the solution to my question. I created a macro:
#define convert(type, func) dynamic_cast<type>(func)
Extremely generic and code-neutral which allows types to be dynamic_casted from the return type of the function. It also allows for doing checks:
if (!convert(ShaderProgram*, rm::get("skyboxShader")))
cerr << "Conversion unsuccessful!" << endl;
else cout << "Conversion successful!" << endl;
I hope my solution will help people who search for questions similar of this kind. Thanks all!

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

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.

Is there an idiomatic way to create a collection of delegates in C++?

I want to store functions with similar signature in a collection to do something like this:
f(vector<Order>& orders, vector<Function>& functions) {
foreach(process_orders in functions) process_orders(orders);
}
I thought of function pointers:
void GiveCoolOrdersToBob(Order);
void GiveStupidOrdersToJohn(Order);
typedef void (*Function)(Order);
vector<Function> functions;
functions.push_back(&GiveStupidOrdersToJohn);
functions.push_back(&GiveCoolOrdersToBob);
Or polymorphic function objects:
struct IOrderFunction {
virtual void operator()(Order) = 0;
}
struct GiveCoolOrdersToBob : IOrderFunction {
...
}
struct GiveStupidOrdersToJohn : IOrderFunction {
...
}
vector<IOrderFunction*> functions;
functions.push_back(new GiveStupidOrdersToJohn());
functions.push_back(new GiveCoolOrdersToBob());
Premise:
The design you propose will work, but using regular function pointers will limit you considerably in the kind of callbacks you can register, and although more powerful, the approach based on inheritance from a fixed interface is more verbose and requires more work for a client to define callbacks.
In this answer I will first show some examples of how to use std::function for this purpose. The examples will pretty much speak for themselves, showing how and why using std::function brings advantages as opposed to the kind of solutions you outlined.
However, a naive approach based on std::function will also have limitations of its own, which I am going to list. This is why I eventually suggest you to have a look at Boost.Signals2: it is a pretty powerful and easy-to-use library. I will address Boost.Signals2 at the end of this answer. Hopefully, understanding a simple design based on std::function first will make it easier for you to grasp the more complex aspects of signals and slots later on.
Solution based on std::function<>
Let's introduce a couple of simple classes and prepare the ground for some concrete examples. Here, an order is something which has an id and contains several items. Each item is described by a type (for simplicity, here it can be either a book a dvd), and a name:
#include <vector>
#include <memory>
#include <string>
struct item // A very simple data structure for modeling order items
{
enum type { book, dvd };
item(type t, std::string const& s) : itemType(t), name(s) { }
type itemType; // The type of the item
std::string name; // The name of the item
};
struct order // An order has an ID and contains a certain number of items
{
order(int id) : id(id) { }
int get_id() const { return id; }
std::vector<item> const& get_items() const { return items; }
void add_item(item::type t, std::string const& n)
{ items.emplace_back(t, n); }
private:
int id;
std::vector<item> items;
};
The heart of the solution I am going to outline is the following class order_repository, and its internal usage of std::function to hold callbacks registered by clients.
Callbacks can be registered through the register_callback() function, and (quite intuitively) unregistered through the unregister_callback() function by providing the cookie returned by registered_callback() upon registration:
The function than has a place_order() function for placing orders, and a process_order() function that triggers the processing of all orders. This will cause all of the registered handlers to be invoked sequentially. Each handler receives a reference to the same vector of placed orders:
#include <functional>
using order_ptr = std::shared_ptr<order>; // Just a useful type alias
class order_repository // Collects orders and registers processing callbacks
{
public:
typedef std::function<void(std::vector<order_ptr>&)> order_callback;
template<typename F>
size_t register_callback(F&& f)
{ return callbacks.push_back(std::forward<F>(f)); }
void place_order(order_ptr o)
{ orders.push_back(o); }
void process_all_orders()
{ for (auto const& cb : callbacks) { cb(orders); } }
private:
std::vector<order_callback> callbacks;
std::vector<order_ptr> orders;
};
The strength of this solution comes from the use of std::function to realize type erasure and allow encapsulating any kind of callable object.
The following helper function, which we will use to generate and place some orders, completes the set up (it simply creates four orders and adds a few items to each order):
void generate_and_place_orders(order_repository& r)
{
order_ptr o = std::make_shared<order>(42);
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
o = std::make_shared<order>(1729);
o->add_item(item::book, "TC++PL, 4th Edition");
o->add_item(item::book, "C++ Concurrency in Action");
r.place_order(o);
o = std::make_shared<order>(24);
o->add_item(item::dvd, "2001: A Space Odyssey");
r.place_order(o);
o = std::make_shared<order>(9271);
o->add_item(item::dvd, "The Big Lebowski");
o->add_item(item::book, "C++ Concurrency in Action");
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
}
Now let's see what kinds of callback we can provide. For starter, let's have a regular callback function that prints all of the orders:
void print_all_orders(std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id() << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
std::cout << "=========================\n\n";
}
And a simple program that uses it:
int main()
{
order_repository r;
generate_and_place_orders(r);
// Register a regular function as a callback...
r.register_callback(print_all_orders);
// Process the order! (Will invoke all the registered callbacks)
r.process_all_orders();
}
Here is the live example showing the output of this program.
Quite reasonably, you are not limited to registering regular functions only: any callable object can be registered as a callback, including a functor holding some state information. Let's rewrite the above function as a functor which can either print the same detailed list of orders as function print_all_orders() above, or a shorter summary that does not include order items:
struct print_all_orders
{
print_all_orders(bool detailed) : printDetails(detailed) { }
void operator () (std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id();
if (printDetails)
{
std::cout << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
else { std::cout << std::endl; }
}
std::cout << "=========================\n\n";
}
private:
bool printDetails;
};
Here is how this could be used in a small test program:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
// Register one particular instance of our functor...
r.register_callback(print_all_orders(false));
// Register another instance of the same functor...
r.register_callback(print_all_orders(true));
r.process_all_orders();
}
And here is the corresponding output shown in this live example.
Thanks to the flexibility offered by std::function, we can also register the result of std::bind() as a callback. To demonstrate this with an example, let's introduce a further class person:
#include <iostream>
struct person
{
person(std::string n) : name(n) { }
void receive_order(order_ptr spOrder)
{ std::cout << name << " received order " << spOrder->get_id() << std::endl; }
private:
std::string name;
};
Class person has a member function receive_order(). Invoking receive_order() on a certain person object models the fact that a particular order has been delivered to that person.
We could use the class definition above to register a callback function that dispatches all the orders to one person (which can be determined at run-time!):
void give_all_orders_to(std::vector<order_ptr>& orders, person& p)
{
std::cout << "Dispatching orders:\n=========================\n";
for (auto const& o : orders) { p.receive_order(o); }
orders.clear();
std::cout << "=========================\n\n";
}
At this point we could write the following program, that register two callbacks: the same function for printing orders we have used before, and the above function for dispatching orders to a certain instance of Person. Here is how we do it:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
person alice("alice");
r.register_callback(print_all_orders);
// Register the result of binding a function's argument...
r.register_callback(std::bind(give_all_orders_to, _1, std::ref(alice)));
r.process_all_orders();
}
The output of this program is shown in this live example.
And of course one could use lambdas as callbacks. The following program builds on the previous ones to demonstrate the usage of a lambda callback that dispatches small orders to one person, and large orders to another person:
int main()
{
order_repository r;
generate_and_place_orders(r);
person alice("alice");
person bob("bob");
r.register_callback(print_all_orders);
r.register_callback([&] (std::vector<order_ptr>& orders)
{
for (auto const& o : orders)
{
if (o->get_items().size() < 2) { bob.receive_order(o); }
else { alice.receive_order(o); }
}
orders.clear();
});
r.process_all_orders();
}
Once again, this live example shows the corresponding output.
Beyond std::function<> (Boost.Signals2)
The above design is relatively simple, quite flexible, and easy to use. However, there are many things it does not allow to do:
it does not allow to easily freeze and resume the dispatching of events to a particular callback;
it does not encapsulate sets of related callbacks into an event
class;
it does not allow grouping callbacks and ordering them;
it does not allow callbacks to return values;
it does not allow combining those return values.
All these feature, together with many others, are provided by full-fledged libraries such as Boost.Signals2, which you may want to have a look at. Being familiar with the above design, it will be easier for you to understand how it works.
For instance, this is how you define a signal and register two simple callbacks, and call them both by invoking the signal's call operator (from the linked documentation page):
struct Hello
{
void operator()() const
{
std::cout << "Hello";
}
};
struct World
{
void operator()() const
{
std::cout << ", World!" << std::endl;
}
};
int main()
{
boost::signals2::signal<void ()> sig;
sig.connect(Hello());
sig.connect(World());
sig();
}
As usual, here is a live example for the above program.
You might want to look into std::function, your vector would then look like this:
std::vector< std::function< void( Order ) > > functions;
But be aware that std::function has a small overhead. For the instances, drop the new:
function.push_back(GiveStupidOrdersToJohn());
Boost.Signal solves exactly your problem. You should have a look into that. Unless you have special requirements. In particular boost.signal and boost.function and/or std::function
use type erasure techniques. Thus, you are able to have a vector of callable things with a specified signature. It does not matter if your entities are plain C-functions (as you have in your example) or function-objects or member-functions in general. You can mix all of them.