Generic messaging - c++

I am working on a messaging system on C++. I have;
class MessageData
{
public:
typedef std::vector<std::shared_ptr<MessageData>> MessageList;
virtual int getValue(std::shared_ptr<int>) { throw "Not implemented!"; };
virtual float getValue(std::shared_ptr<float>) { throw "Not implemented!"; };
virtual std::string getValue(std::shared_ptr<std::string>) { throw "Not implemented!"; };
...
...
virtual ~MessageData() {};
};
template <typename T>
class Message : public MessageData
{
T val;
public:
static std::shared_ptr<Message<T>> Make(T val) { return std::make_shared<Message<T>>(val); };
static T Get(std::shared_ptr<MessageData> in) { return in->getValue(std::make_shared<T>()); };
Message(T i) { val = i; };
T getValue(std::shared_ptr<T> out) override { return *out = val; }
~Message() {};
};
Using these, I can send/receive generic messages of different length conveniently using e.g;
sendMessage(MessageData::MessageList{
Message<std::string>::Make("paint"),
Message<int>::Make(14),
Message<float>::Make(129.3f),
...
});
Then I get the values;
sendMessage(MessageData::MessageList data) {
auto a = Message<std::string>::Get(data[0]);
auto b = Message<int>::Get(data[1]);
auto c = Message<float>::Get(data[2]);
...
}
The downside is that I have to list all the types I need to use in MessageData class. This isn't a big deal as I can limit the types I want to support but I'm really curious about how to templatize the type list without using a 3rd party library. Or is there a completely different and better method that I can use with similar clean syntax and type safety to pass messages around?

One way to make your code more generic is:
template <typename ... Ts>
class MessageDataImp;
template <typename T>
class MessageDataImp<T>
{
public:
virtual ~MessageDataImp() = default;
virtual T getValue(std::shared_ptr<T>) { throw "Not implemented!"; };
};
template <typename T, typename ... Ts>
class MessageDataImp<T, Ts...> : public MessageDataImp<T>, public MessageDataImp<Ts...>
{
public:
using MessageDataImp<T>::getValue;
using MessageDataImp<Ts...>::getValue;
};
template <typename ... Ts>
class MessageDataTs : public MessageDataImp<Ts...>
{
public:
typedef std::vector<std::shared_ptr<MessageDataTs<Ts...>>> MessageList;
};
using MessageData = MessageDataTs<int, float, std::string>;

I think I've developed a decent solution to my problem.
class MessageData {
public:
typedef std::vector<std::shared_ptr<MessageData>> MessageList;
virtual ~MessageData() {};
};
template<typename T>
class Message : public MessageData {
T val;
public:
template<typename U>
friend U GetMessage(std::shared_ptr<MessageData> in);
Message(T i) { val = i; };
};
template<typename T>
T GetMessage(std::shared_ptr<MessageData> in) {
std::shared_ptr<Message<T>> tmp = std::dynamic_pointer_cast<Message<T>>(in);
if (tmp) {
return tmp->val;
}
throw "Incorrect type!";
};
template<typename T>
std::shared_ptr<Message<T>> MakeMessage(T val)
{
return std::make_shared<Message<T>>(val);
};
Then send & receive values using;
sendMessage(MessageData::MessageList{
MakeMessage(std::string("paint")),
MakeMessage(14),
MakeMessage(129.3f),
...
});
sendMessage(MessageData::MessageList data) {
auto a = GetMessage<std::string>(data[0]);
auto b = GetMessage<int>(data[1]);
auto c = GetMessage<float>(data[2]);
...
}

Assuming that it's a simple multiple-reader, multiple-writer message bus based on a non-prioritised queue, I think I'd start with something like this:-
Note that I have used boost::variant/optional. These could easily be replaced with std:: versions if you have those available.
I have used variant because it efficiently caters for most use cases with compile-time safety.
The std/boost::any version would require significant (and possibly unwelcome) care for users of your bus.
#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <condition_variable>
#include <boost/variant.hpp>
#include <boost/optional.hpp>
template<class Mutex> auto get_lock(Mutex& m) { return std::unique_lock<Mutex>(m); }
template<class...Types>
struct message_bus
{
using message_type = boost::variant<Types...>;
void push(message_type msg) {
auto lock = get_lock(mutex_);
messages_.push(std::move(msg));
lock.unlock();
activity_.notify_one();
}
boost::optional<message_type> wait_pop()
{
boost::optional<message_type> result;
auto lock = get_lock(mutex_);
activity_.wait(lock, [this] { return this->stopped_ or not this->messages_.empty(); });
if (not messages_.empty())
{
result = std::move(messages_.front());
messages_.pop();
}
return result;
}
void signal_stop()
{
auto lock = get_lock(mutex_);
stopped_ = true;
lock.unlock();
activity_.notify_all();
}
std::queue<message_type> messages_;
std::mutex mutex_;
std::condition_variable activity_;
bool stopped_ = false;
};
static std::mutex emit_mutex;
template<class T>
void emit(const T& t)
{
auto lock = get_lock(emit_mutex);
std::cout << std::this_thread::get_id() << ": " << t << std::endl;;
}
int main()
{
using bus_type = message_bus<std::string, int>;
bus_type mb;
std::vector<std::thread> threads;
for (int i = 0 ; i < 10 ; ++i)
{
threads.emplace_back([&]
{
for(;;)
{
auto message = mb.wait_pop();
if (not message)
break;
boost::apply_visitor([](auto&& data) { emit(data); }, message.value());
}
});
}
for (int i = 0 ; i < 1000 ; ++i)
{
mb.push("string: " + std::to_string(i));
mb.push(i);
}
mb.signal_stop();
for (auto& t : threads) if (t.joinable()) t.join();
}

Related

Which pattern to generify instantiation of objects that have different Ctor arguments

I would like to construct a robot with or without a tool, a mobile base, and other parts. As I want to automatize the configuration of the parts, I have a class Robot with the parts as template arguments
For instance, in the code below, the code will build as long as we use tools that have the same constructor signature as ToolInterface. It does build with a Screwdriver but does not with a Gripper.
#include <iostream>
#include <string>
class BaseRobot
{
public:
BaseRobot(){};
};
class ToolInterface
{
public:
ToolInterface(BaseRobot* _base, std::string _name):name{_name}{/*register _base*/};
std::string name;
bool param_1;
char param_2;
};
template<class T, class... Args>
constexpr T* construct(Args... args)
{
if constexpr (std::is_same<T, nullptr_t>::value)
{
return nullptr;
}
else
{
return new T(args...);
}
};
template<class Tool>
class Robot : public BaseRobot
{
protected:
Tool* tool;
public:
Robot():tool(construct<Tool>(this, "tool")){ // <--- here is my problem !!
if constexpr (! std::is_same<Tool, nullptr_t>::value)
{
//do stuff on/with tool->param_1, tool->param_2, ...
std::cout << "tool configured" << std::endl;
}
else
std::cout << "no tool" << std::endl;
};
};
class Screwdriver: public ToolInterface
{
public:
Screwdriver(BaseRobot* _base, std::string _name):ToolInterface(_base, _name){};
};
class Gripper: public ToolInterface
{
public:
Gripper(BaseRobot* _base, std::string _name, bool _reversed):
ToolInterface(_base, _name)
,reversed{_reversed}{};
bool reversed;
};
int main()
{
Robot<Screwdriver> robot_screwdriver;
Robot<nullptr_t> robot_null;
//Robot<Gripper> robot_gripper; //does not build
return 0;
}
Here are some ideas :
using a ToolConfig struct that is passed as an argument of Tools. If a tool requires more arguments, one should subclass ToolConfig and cast it into the tool constructor (see below): damn, that looks cumbersome and ugly!
enforce inherited ToolInterface classes Ctor signature: some tools must have a different Ctor signature
using a variadic template to pass args into the template: not reasonable because, in the end, I want something like template<class Tool1, class Tool2, class MobileBase, class Camera> class Robot
solution 1 would look like
struct ToolConfig
{
std::string name;
};
struct GripperConfig : public ToolConfig
{
bool reversed;
};
class Gripper : public ToolInterface
{
public:
Gripper(ToolConfig& _config):
ToolInterface(_config)
,reversed{static_cast<GripperConfig&>(_config).reversed}{};
bool reversed;
};
Do you have a magic pattern to solve my problem? Is my pattern wrong?
You could also use tuple instead of struct, not ideal but this works as well:
#include <iostream>
#include <string>
#include <tuple>
class BaseRobot
{
public:
BaseRobot() {};
};
class ToolInterface
{
public:
ToolInterface(std::string _name) :name{ _name } {/*register _base*/ };
std::string name;
bool param_1;
char param_2;
};
template <typename T, typename ... Types, std::size_t ... Indices>
constexpr T* apply_impl(const std::tuple<Types...>& tuple, std::index_sequence<Indices...>)
{
return new T(std::get<Indices>(tuple)...);
}
template <typename T, typename ... Types>
constexpr T* apply(const std::tuple<Types...>& tuple)
{
return apply_impl<T>(tuple, std::index_sequence_for<Types...>());
}
template<class T, class... Args>
constexpr T* construct(std::tuple<Args...> args)
{
if constexpr (std::is_same<T, nullptr_t>::value)
{
return nullptr;
}
else
{
return apply<T>(args);
}
}
template<class Tool>
class Robot : public BaseRobot
{
protected:
Tool* tool;
public:
template<class ...Args1> //, class ...Args2>
Robot(std::tuple<Args1...> p1): // , std::tuple<Args2...> p2):
tool(construct<Tool>(p1))
{ // <--- here is my problem !!
if constexpr (!std::is_same<Tool, nullptr_t>::value)
{
//do stuff on/with tool->param_1, tool->param_2, ...
std::cout << "tool configured" << std::endl;
}
else
std::cout << "no tool" << std::endl;
};
};
class Screwdriver : public ToolInterface
{
public:
Screwdriver(std::string _name) :ToolInterface(_name) {};
};
class Gripper : public ToolInterface
{
public:
Gripper(std::string _name, bool _reversed) :
ToolInterface(_name)
, reversed{ _reversed }{};
bool reversed;
};
int main()
{
using p1 = std::tuple<std::string>;
Robot<Screwdriver> robot_screwdriver(p1{"sdvr"});
return 0;
}
Could be improved I agree.
You could pass factory lambdas that generate your tools in the initializer.
template<typename Func>
Robot(Func f):tool(f(this, "tool")){ // <--- here is my problem !!
if constexpr (! std::is_same<Tool, std::nullptr_t>::value)
{
//do stuff on/with tool->param_1, tool->param_2, ...
std::cout << "tool configured" << std::endl;
}
else
std::cout << "no tool" << std::endl;
};
The call site would look like this:
Robot<Screwdriver> robot_screwdriver([](auto... args){ return new Screwdriver(args...); });
Robot<std::nullptr_t> robot_null([](auto...){ return nullptr; });
Robot<Gripper> robot_gripper([](auto... args){ return new Gripper(args..., true); });
Not exactly beautiful, but it works.
See here for a full example. Does this solve your problem?
If you can use c++17, you can add a class template deduction guide to reduce some of the redundancy at the call site.

C++ Similar functions using different data types

I have two functions which are exactly the same, except that one of them uses a stack for its operations and the other one uses a queue:
void doQueue()
{
std::queue<int> q;
...
...
q.push(someValue);
...
...
int tmp = q.front();
q.pop()
}
void doStack()
{
std::stack<int> s;
...
...
s.push(someValue);
...
...
int tmp = s.top();
s.pop()
}
I want to eliminate duplicate code. As queue uses the front function to retrieve the first value and stack uses the top function, I thought that templates may not work since functions with different names have to be called.
My other idea was to create an interface which will be as a wrapper to both data structures and I can pass around the one that I need.:
class Data
{
public:
virtual void push(const int v) = 0;
virtual int pop() = 0;
};
class StackData : public Data
{
private:
std::stack<int> _stack;
public:
virtual void push(const int v) {_stack.push(v);}
virtual int pop()
{
int ret = _stack.top();
_stack.pop();
return ret;
}
};
class QueueData : public Data
{
private:
std::queue<int> _queue;
public:
virtual void push(const int v) {_queue.push(v);}
virtual int pop()
{
int ret = _queue.front();
_queue.pop();
return ret;
}
};
void doData(Data& dataType)
{
...
dataType.push(someValue);
...
int tmp = dataType.pop();
}
void doQueue()
{
QueueData queueData;
doData(queueData);
}
void doStack()
{
StackData stackData;
doData(stackData);
}
But I think there should be an easier and better way to perform this operation.
Here's one way - a wrapper template with partial specialisation on underlying container type:
#include <stack>
#include <queue>
template<class Container>
struct generic_sequence_ops;
template<class T, class UnderlyingContainer>
struct generic_sequence_ops<std::stack<T, UnderlyingContainer>>
{
using container_type = std::stack<T, UnderlyingContainer>;
using value_type = typename container_type::value_type;
generic_sequence_ops(container_type& c) : c(c) {}
void add_one(value_type v)
{
c.push(std::move(v));
}
void remove_one()
{
c.pop();
}
value_type& current()
{
return c.top();
}
container_type& c;
};
template<class T, class UnderlyingContainer>
struct generic_sequence_ops<std::queue<T, UnderlyingContainer>>
{
using container_type = std::queue<T, UnderlyingContainer>;
using value_type = typename container_type::value_type;
generic_sequence_ops(container_type& c) : c(c) {}
void add_one(value_type v)
{
c.push(std::move(v));
}
void remove_one()
{
c.pop();
}
value_type& current()
{
return c.back();
}
container_type& c;
};
template<class Container>
auto make_generic_sequence_ops(Container& cont)
{
return generic_sequence_ops<std::decay_t<Container>>(cont);
}
template<class Container>
int doContainer(Container& cont)
{
auto s = make_generic_sequence_ops(cont);
s.add_one(6);
int val = s.current();
s.remove_one();
return val;
}
int main()
{
std::queue<int> q;
std::stack<int> s;
doContainer(q);
doContainer(s);
}

How to specify an templatized alias' generic type in a container

I have a class Task:
template <typename T>
class Task {
Task(const std::function<T()>& func)
: m_func(func)
{
// some stuff here
}
std::shared_ptr<T> getValue() {
return m_value;
}
void execute() {
m_value = std::make_shared<T>(m_func());
}
std::shared_ptr<T> m_value;
std::function<T()> m_func;
}
Now, I want to alias this Task class to a shared_ptr so I do the following...
template <typename T> using TaskPtr = std::shared_ptr<Task<T> >;
I have another class that will store a container of of TaskPtr, I would like for the consumer of the api to specify T when calling addTask as follows.
Class X {
// some boiler plate code
template <typename T>
addTask(TaskPtr<T> task) {
m_queue.push(task);
}
void loop() {
// do some stuff
auto item = m_queue.front();
item->execute();
m_queue.pop();
// continue looping
}
std::queue<TaskPtr<T> > m_queue;
}
I was wondering what the best way to do this would be. This code gives me the error that T is undefined. Duh! I need to add template <tyepname T> above my m_queue definition, that makes sense. When I do that, I get that I am putting the keyword typedef in an incorrect location. When I remove the template declaration and the T to just have std::queue<Taskptr> m_queue;, it tells me I am missing a template argument. Which makes sense, except I don't understand where it should go.
I have searched for an answer and couldn't find anything. What is the correct syntactical implementation for what I am trying do?
The error is at:
class X {
....
std::queue<TaskPtr<T> > m_queue; // <--- T is unknown
};
At that point, the compiler wants to know what is the type of the task, but you want simply to store all tasks regardless to their type. To figure out how to make this work, look at the uses of T and see how to get rid of it.
template <typename T>
class Task {
std::shared_ptr<T> getValue() {
return m_value;
}
void execute() {
m_value = std::make_shared<T>(m_func());
}
....
};
Had it been only execute then life would have been simple, sine the caller of execute() does not care what T is, only that the operation is executed. If it were only that, then the solution would have been trivial:
class TaskBase
{
public:
virtual ~TaskBase() = default;
TaskBase(const TaskBase &) = default; // and so on....
virtual void execute() = 0;
};
template <typename T>
class Task : public TaskBase {
....
};
Then, simply store a pointer to TaskBase instead of to Task<T>.
Solving the getValue() is slightly more involved. You need to use dynamic cast from TaskBase to the actual Task from getValue<T>():
template <typename T>
std::shared_ptr<T> Task<T>::getValue() {
return m_value;
}
template<typename T>
std::shared_ptr<T> TaskBase::getValue()
{
auto childThis = dynamic_cast<Task<T>*>(this);
if (childThis == nullptr) {
// or maybe throw an exception
return nullptr;
}
return childThis->getValue();
}
The use is more tricky, since the user has to know what type is stored in the task:
void foo(std::shared_ptr<TaskBase> ptr)
{
auto ifInt = ptr->getValue<int>();
auto ifDouble = ptr->getValue<double>();
... more code ..
}
In this case Task<int> would be detected by ifInt, but with Task<unsigned> this would fail since ifInt==nullptr.
Apparently the above explanation is not clear enough, so here is the complete source that compiles and works:
#include <memory>
#include <functional>
#include <queue>
#include <iostream>
class TaskBase
{
public:
virtual ~TaskBase() = default;
TaskBase() = default;
TaskBase(const TaskBase &) = default; // and so on....
virtual void execute() = 0;
template <typename T>
std::shared_ptr<T> getValue();
};
template <typename T>
class Task : public TaskBase {
public:
Task(const std::function<T()>& func)
: m_func(func)
{
// some stuff here
}
void execute() override {
m_value = std::make_shared<T>(m_func());
}
std::shared_ptr<T> getValue() {
return m_value;
}
private:
std::shared_ptr<T> m_value;
std::function<T()> m_func;
};
template <typename T>
std::shared_ptr<T> TaskBase::getValue()
{
auto downCast = dynamic_cast<Task<T>*>(this);
if (downCast)
return downCast->getValue();
else
return nullptr;
}
using TaskPtr = std::shared_ptr<TaskBase>;
class X {
// some boiler plate code
public:
void addTask(TaskPtr task) {
m_queue.push(task);
}
void loop() {
// do some stuff
auto item = m_queue.front();
item->execute();
m_queue.pop();
// continue looping
}
std::queue<TaskPtr> m_queue;
};
int main()
{
X x;
TaskPtr task = std::make_shared<Task<int>>(
[] { std::cout << "int task execution\n"; return 5;});
x.addTask(task);
x.loop();
std::cout << "getValue<int> --> ";
auto valPtr = task->getValue<int>();
if (valPtr)
std::cout << *valPtr << '\n';
else
std::cout << "nullptr\n";
std::cout << "getValue<float> --> ";
auto valPtr2 = task->getValue<float>();
if (valPtr2)
std::cout << *valPtr2 << '\n';
else
std::cout << "nullptr\n";
}

Store different templated classes in one container without losing information about it's type

I'm currently working on a project where a client part of my application has to be able to create custom templated classes on the server. The server part has to keep track of these created classes and has to remember the types with which the classes has been instantiated. The problem is, that there are around 36 different class-template-combinations that are valid in my application. I'm currently struggling to keep track of these different types in a collection without losing information about my instances.
I'm currently using something like this:
#include <memory>
#include <type_traits>
#include <vector>
enum class data_type : std::uint8_t {
type_int = 1,
type_float,
type_double
};
enum class class_type : std:: uint8_t {
type_A = 1,
type_B
};
struct X {
virtual data_type get_data_type() = 0;
virtual class_type get_class_type() = 0;
};
template <typename T>
struct A : X {
data_type get_data_type() override
{
if (std::is_same<T, int>::value) {
return data_type::type_int;
} else if (std::is_same<T, float>::value) {
return data_type::type_float;
} else if (std::is_same<T, double>::value) {
return data_type::type_double;
} else {
/* ... */
}
}
class_type get_class_type() override
{
return class_type::type_A;
}
};
template <typename T>
struct B : X {
data_type get_data_type() override
{
if (std::is_same<T, int>::value) {
return data_type::type_int;
} else if (std::is_same<T, float>::value) {
return data_type::type_float;
} else if (std::is_same<T, double>::value) {
return data_type::type_double;
} else {
/* ... */
}
}
class_type get_class_type() override
{
return class_type::type_B;
}
};
struct Storage {
template <typename T, template <typename> class Class>
void create() {
Class<T>* t = new Class<T>();
_classes.push_back(std::unique_ptr<X>(t));
}
std::vector<std::unique_ptr<X>> _classes;
};
but I'm wondering if this is the way to go or if there is a more elegant way. Here I would have to always switch through the enums to get the full type out of my Storage class, something like:
switch(_classes.front()->get_class_type()) {
case class_type::type_A:
{
switch(_classes.front()->get_data_type()) {
case data_type::type_int:
{
/* I finally know that it is A<int> */
}
/* ... */
Thanks in advance.
You can consider using std::variant and the std::visit pattern
auto var = std::variant<int, float, double>{};
// assign var to value
std::visit([](auto& value) {
using Type = std::decay_t<decltype(value)>;
if constexpr (std::is_same<Type, int>{}) {
// is an int
} else if (std::is_same<Type, float>{}) {
// is float
} else if (std::is_same<Type, double>{}) {
// is double
}
}, var);
If the if constexpr looks ugly to you then you can substitute it with a handrolled visitor class as well.
class Visitor {
public:
void operator()(int& value) { ... }
void operator()(float& value) { ... }
void operator()(double& value) { ... }
};
auto var = std::variant<int, float, double>{};
// assign var to value
std::visit(Visitor{}, var);
As mentioned in the comments to the question, this is a viable approach that could help:
#include<vector>
#include<memory>
struct Counter {
static int next() {
static int v = 0;
return v++;
}
};
template<typename>
struct Type: Counter {
static int value() {
static const int v = Counter::next();
return v;
}
};
struct X {
virtual int get_data_type() = 0;
virtual int get_class_type() = 0;
};
template <typename T>
struct A : X {
int get_data_type() override {
return Type<T>::value();
}
int get_class_type() override {
return Type<A<T>>::value();
}
};
template <typename T>
struct B : X {
int get_data_type() override {
return Type<T>::value();
}
int get_class_type() override {
return Type<B<T>>::value();
}
};
struct Storage {
template <typename T, template <typename> class Class>
void create() {
Class<T>* t = new Class<T>();
_classes.push_back(std::unique_ptr<X>(t));
}
std::vector<std::unique_ptr<X>> _classes;
};
int main() {
Storage s;
s.create<int, A>();
if(Type<int>::value() == s._classes.front()->get_class_type()) {
//...
};
}
See it running on wandbox.

For range loop for a non copyable type, is it possible?

I have some duplicated code in which I read from two streams,
{
std::ifstream ifs("A.dat");
... code ...
}
{
std::ifstream ifs("B.dat");
... same code ...
}
I wanted to unify both in one loop.
The first reaction is to do this:
for(auto ifs : {ifstream("A.dat"), ifstream("B.dat")})
{
... code ...
}
However it doesn't compile because the type is not copyable, so I tried this:
for(auto& ifs : {ifstream("A.dat"), ifstream("B.dat")})
{
... code ...
}
that doesn't work because ifs inside the loop is const. (a const ifstream cannot be used.)
This didn't work either, I think for the same reason:
for(auto&& ifs : {ifstream("A.dat"), ifstream("B.dat")})
At the end of course I ended up doing this.
#include<iostream>
int main(){
for(auto& name : {"A.dat", "B.dat"})
{
std::ifstream ifs(name);
... code ...
}
But I am still curious if it is possible to have range for-loop directly with a type like std::ifstream?
std::ifstream streams[2];
streams[0].open("A.dat");
streams[1].open("B.dat");
for (auto &stream:streams)
{
// ...
}
In c++, everything is possible.
Imagine being able to iterator a collection of any kind of stream, like this:
int main()
{
std::string buffer;
for (auto& stream : streams(std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt")))
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
}
}
Well now you can. Behold: a polymorphic temporary container and iterator for any istream.
A similar technique will work for any arbitrary collection of objects that share a common polymorphic interface.
#include <iostream>
#include <fstream>
#include <sstream>
#include <utility>
#include <tuple>
#include <array>
namespace detail {
template<class Interface>
struct iface_iter
{
using value_type = Interface;
using reference = Interface&;
using internal_p = value_type * const *;
iface_iter(internal_p pp) : _pp(pp) {}
reference operator*() const {
return **_pp;
}
iface_iter& operator++() {
++_pp;
return *this;
}
bool operator==(const iface_iter& r) const {
return _pp == r._pp;
}
bool operator!=(const iface_iter& r) const {
return !(*this == r);
}
internal_p _pp;
};
template<class CommonType, class...Streams>
struct common_sequence
{
using common_type = CommonType;
using iterator = iface_iter<common_type>;
constexpr static std::size_t size() { return sizeof...(Streams); }
using storage_type = std::tuple<Streams...>;
using pointer_array = std::array<common_type*, size()>;
common_sequence(Streams...streams)
: _storage(std::move(streams)...)
, _pointers(build_pointers(std::make_index_sequence<size()>(), _storage))
{}
common_sequence(common_sequence&& r)
: _storage(std::move(r._storage))
, _pointers(build_pointers(std::make_index_sequence<size()>(), _storage))
{
}
common_sequence& operator=(common_sequence&& r)
{
_storage = std::move(r._storage);
_pointers = build_pointers(std::make_index_sequence<size()>(), _storage);
}
template<std::size_t I>
using stream_type = std::tuple_element_t<I, storage_type>;
template<std::size_t...Is>
static constexpr
pointer_array build_pointers(std::index_sequence<Is...>,
std::tuple<Streams...>& tup)
{
return pointer_array {
static_cast<common_type*>(&static_cast<stream_type<Is>&>(std::get<Is>(tup)))...
};
}
iterator begin() const {
return { _pointers.data() };
}
iterator end() const {
return { _pointers.data() + size() };
}
mutable storage_type _storage;
pointer_array _pointers;
};
}
template<class CommonBase, class...Things>
auto make_common_sequence(Things&&...ts)
{
return detail::common_sequence<CommonBase, std::decay_t<Things>...>(std::move(ts)...);
}
template<class...Streams>
auto streams(Streams&&...strs)
{
return make_common_sequence<std::istream>(std::move(strs)...);
}
struct base
{
virtual void foo() = 0;
};
struct d1 : base
{
void foo() override { std::cout << "d1::foo" << std::endl; }
};
struct d2 : base
{
void foo() override { std::cout << "d2::foo" << std::endl; }
};
template<class...Ts>
auto bases(Ts&&...ts)
{
return make_common_sequence<base>(std::move(ts)...);
}
int main()
{
std::string buffer;
for (auto& stream : streams(std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt")))
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
}
for (auto& f : bases(d1(), d2(), d1(), d2()))
{
f.foo();
}
return 0;
}
expected output:
hello
world
funky
chicken
... plus whatever is in foo.txt ...
d1::foo
d2::foo
d1::foo
d2::foo
Of course, if we don't require polymorphism, a simple template variadic argument iterator will suffice:
template<class F, class...Things>
void apply_to_all(F f, Things... things)
{
using expand = int[];
void(expand{ 0,
(f(things), 0)...
});
}
int main()
{
std::string buffer;
apply_to_all([&](auto& stream)
{
while (stream >> buffer)
{
std::cout << buffer << std::endl;
}
},
std::istringstream("hello world"),
std::istringstream("funky chicken"),
std::ifstream("foo.txt"));
}
Inspired by #SamVarshavchik's answer I found that this works:
for (auto& ifs: std::array<std::ifstream, 2>{std::ifstream("A.dat"), std::ifstream("B.dat")} )
{
// ...
}
With std::make_array from TS2 (http://en.cppreference.com/w/cpp/experimental/make_array) this will work too:
for (auto& ifs: std::make_array(std::ifstream("A.dat"), std::ifstream("B.dat")) )
{
// ...
}
And with a further hack
template < class TT, class... Types>
constexpr std::array<TT, sizeof...(Types)> make_array_of(Types&&... t) {
return {TT(std::forward<Types>(t))... };
}
I can do
for (auto& ifs: make_array_of<std::ifstream>("A.dat", "B.dat") ){
...
}