Advantages/Disadvantages of Many Nested Callbacks? - c++

I have a work distribution scheme where every unit does some book-keeping and management and pass the task to next one in order chain such as; Lets say have 3 classes: Boss, Manager, Worker
class Boss
{
void do_async(Request req, std::function<void(Result)> callback)
{
//Find eligible manager etc.
manager.do_async(Boss_request(req,...),std::bind(&Boss::callback,this,callback,std::placeholders::_1));
}
void callback(std::function<void(Result)> main_callback,Boss_result res)
{
//some book keeping
main_callback(res.main_part);
}
};
class Manager
{
void do_async(Boss_request req, std::function<void(Boss_result)> boss_callback)
{
//Find eligible worker etc. add some data to request
worker.do_async(Manager_request(req,...),std::bind(&Manager::callback,this,boss_callback,std::placeholders::_1));
}
void callback(std::function<void(Boss_result)> boss_callback,Manager_result res)
{
//some book keeping
boss_callback(res.boss_part);
}
};
class Worker
{
void do_async(Manager_request req, std::function<void(Manager_result)> manager_callback)
{
//Do job async
work_async(Worker_task(req,...),std::bind(&Worker::callback,this,manager_callback,std::placeholders::_1));
}
void callback(std::function<void(Manager_result)> manager_callback,Worker_result res)
{
//some book keeping
manager_callback(res.manager_part);
}
};
As you can see I am extensively using std::bind, std::function and std::placeholder. Does this approach has any advantages/disadvantages? if not preferable, so what is the better way to do it? Would using lambda functions be possible/preferable(as performance or code-quality) in this state?
Edit: Why do I need asynchronous access in every level, instead of only first level? Because between each classes there is a many-to-many relationship. I have couple of layers of processing units (Boss or Manager or Worker) which can order anyone in next layer. When a unit orders the job to next one in line. It must be free immediately to take new orders from above.
I haven't used directly lambda because callbacks can be a little bit large and may make it harder to read. But code-quality can be sacrificed if there is any significant performance penalty.

What you are doing here is piping data around. Embrace the pipe.
namespace chain {
template<class T, class Base=std::function<void(T)>>
struct sink:Base{
using Base::operator();
using Base::Base;
};
template<class T, class F>
sink<T> make_sink( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
using source=sink<sink<T>>;
template<class T, class F>
source<T> make_source( F&& f ) {
return {std::forward<F>(f)};
}
template<class T>
source<std::decay_t<T>> simple_source( T&& t ) {
return [t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
};
}
template<class In, class Out>
using pipe = std::function< void(source<In>, sink<Out>) >;
template<class In, class Out>
sink<In> operator|( pipe<In, Out> p, sink<Out> s ) {
return [p,s]( In in ) {
p( [&]( auto&& sink ){ sink(std::forward<In>(in)); }, s );
};
}
template<class In, class Out>
source<Out> operator|( source<In> s, pipe<Out> p ) {
return [s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
};
}
template<class T>
std::function<void()> operator|( source<T> in, sink<T> out ) {
return [in, out]{ in(out); };
}
template<class In, class Mid, class Out>
pipe<In, Out> operator|( pipe<In, Mid> a, pipe<Mid, Out> b ) {
return [a,b]( source<In> src, sink<Out> dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
};
}
}//namespace
Then write these:
pipe<Request, Result> Boss::work_pipe();
pipe<Boss_request, Boss_result> Manager::work_pipe();
pipe<Boss_request, Manager_request> Manager::process_request();
pipe<Manager_request, Manager_result> Manager::do_request();
pipe<Manager_result, Boss_results> Manager::format_result();
pipe<Manager_request, Manager_result> Worker::work_pipe();
and similar for Worker and Boss.
pipe<Request, Result> Boss::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Boss_request, Boss_result> Manager::work_pipe() {
return process_request() | do_request() | format_result();
}
pipe<Manager_request, Manager_result> Worker::work_pipe() {
return process_request() | do_request() | format_result();
}
then:
pipe<Manager_request, Manager_result> Manager::do_request() {
return [this]( source<Manager_request> src, sink<Manager_result> dest ) {
// find worker
worker.do_request( src, dest );
};
}
pipe<Manager_output, Boss_result> Manager::format_result() {
return [this]( source<Manager_output> src, sink<Boss_result> dest ) {
src([&]( Manager_output from_worker ) {
// some book keeping
dest( from_worker.boss_part );
});
};
}
now, I made sources "sinks for sinks", because it permits a source (or a pipe) to generate 1, 0, or many messages from one invocation. I find this useful in many cases, but it does make writing pipes a bit stranger.
You can also write this in c++14 without using std::function at all, by simply applying "i am a sink" and "i am a source" and "i am a pipe" tags to lambdas (via composition, like override) then blindly hooking things up with | and hoping their type are compatible.
To do_sync, you just do this:
void Boss::do_async( Request req, sink<Result> r ) {
work_async( simple_source(req) | work_pipe() | r );
}
ie, the entire computation can be bundled up and moved around. This moves the threading work to the top.
If you need the async thread implementation to be at the bottom, you can pipe up the earlier work and pass it down.
void Boss::do_async( source<Request> req, sink<Result> r ) {
find_manager().do_async( req|process_request(), format_result()|r );
}
void Manager::do_async( source<Boss_request> req, sink<Boss_result> r ) {
find_worker().do_async( req|process_request(), format_result()|r );
}
void Worker::do_async( source<Manager_request> req, sink<Manager_result> r ) {
work_async( req|process_request()|do_request()|format_result()|r );
}
because of how the sink/source/pipes compose, you can choose what parts of the composition you pass down and which parts you pass up.
The std::function-less version:
namespace chain {
struct pipe_tag{};
struct sink_tag{};
struct source_tag{};
template<class T, class=void>
struct is_source:std::is_base_of<source_tag, T>{};
template<class T, class=void>
struct is_sink:std::is_base_of<sink_tag, T>{};
template<class T, class=void>
struct is_pipe:std::is_base_of<pipe_tag, T>{};
template<class F, class Tag>
struct tagged_func_t: F, Tag {
using F::operator();
using F::F;
tagged_func_t(F&& f):F(std::move(f)) {}
};
template<class R, class...Args, class Tag>
struct tagged_func_t<R(*)(Args...), Tag>: Tag {
using fptr = R(*)(Args...);
fptr f;
R operator()(Args...args)const{
return f( std::forward<Args>(args)... );
}
tagged_func_t(fptr fin):f(fin) {}
};
template<class Tag, class F>
tagged_func_t< std::decay_t<F>, Tag >
tag_func( F&& f ) { return {std::forward<F>(f)}; }
template<class F>
auto as_pipe( F&& f ) { return tag_func<pipe_tag>(std::forward<F>(f)); }
template<class F>
auto as_sink( F&& f ) { return tag_func<sink_tag>(std::forward<F>(f)); }
template<class F>
auto as_source( F&& f ) { return tag_func<source_tag>(std::forward<F>(f)); }
template<class T>
auto simple_source( T&& t ) {
return as_source([t=std::forward<T>(t)]( auto&& sink ) {
return sink( t );
});
}
template<class Pipe, class Sink,
std::enable_if_t< is_pipe<Pipe>{} && is_sink<Sink>{}, bool> = true
>
auto operator|( Pipe p, Sink s ) {
return as_sink([p,s]( auto&& in ) {
p( [&]( auto&& sink ){ sink(decltype(in)(in)); }, s );
});
}
template<class Source, class Pipe,
std::enable_if_t< is_pipe<Pipe>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source s, Pipe p ) {
return as_source([s,p]( auto&& sink ) {
p( s, decltype(sink)(sink) );
});
}
template<class Source, class Sink,
std::enable_if_t< is_sink<Sink>{} && is_source<Source>{}, bool> = true
>
auto operator|( Source in, Sink out ) {
return [in, out]{ in(out); };
}
template<class PipeA, class PipeB,
std::enable_if_t< is_pipe<PipeA>{} && is_pipe<PipeB>{}, bool> = true
>
auto operator|( PipeA a, PipeB b ) {
return as_pipe([a,b]( auto&& src, auto&& dest ) {
b( src|a, dest );
// or a( src, b|dest );
// but I find pipe|sink -> sink to be less pleasing an implementation
});
}
template<class T>
using sink_t = tagged_func_t< std::function<void(T)>, sink_tag >;
template<class T>
using source_t = tagged_func_t< std::function<void(sink_t<T>)>, source_tag >;
template<class In, class Out>
using pipe_t = tagged_func_t< std::function<void(source_t<In>, sink_t<Out>)>, pipe_tag >;
}
which does fewer type checks, but gets rid of type erasure overhead.
The sink_t, source_t and pipe_t typedefs are useful when you need to type-erase them.
"Hello world" example using the non-type erasure version.

Related

Is there a better way to chain together functions on streams?

I have a series of functions that takes a stream as input and writes a transformation to an output stream. Right now the interface looks like this:
void phase1(std::istream& in, std::ostream& out);
void phase2(std::istream& in, std::ostream& out);
std::istream data = get_initial_data();
std::stringstream first_result;
phase1(data, first_result);
std::stringstream second_result;
phase2(first_result, second_result);
Is there an easier/more natural way to chain these calls without using Boost (sorry)?
I think you'd want to do:
(phase1 | phase2 | phase3)( in, out );
where all the glue happens for you. What more,
auto first_part = phase1|phase2;
auto second_part = phase3|phase4;
(first_part | second_part)( in, out );
should also work.
namespace stream {
template<class F=std::function<void(std::istream&, std::ostream&)>>
struct pipe {
F f;
void operator()( std::istream& in, std::ostream& out ) const {
f(in,out);
}
template<class O,
std::enable_if_t< !std::is_same<O, F>{} && std::is_convertible<O, F>{}, bool> = true
>
pipe ( pipe <O> o ):
f(std::move(o.f))
{}
pipe (F fin):
f(std::move(fin))
{}
};
template<class F>
pipe (F)->pipe <F>;
template<class First, class Second>
auto operator|( pipe <First> first, pipe <Second> second )
{
return pipe {[=](auto& in, auto& out){
std::stringstream intermediate;
first( in, intermediate );
second( intermediate, out );
}};
}
}
and now you can do:
std::istream data = get_initial_data();
( pipe {phase1} | pipe {phase2} )( data, out );
we can extend this to sources and sinks, allowing things to be glued to the input, but that often requires continuation passing style to handle lifetime issues.
You an also use pipe <> to handle any stream pipe object in a type-erased manner.
Live example.
If you want sources and sinks it looks like this:
namespace stream {
template<class Sig, class F=std::function<Sig>>
struct operation;
template<class R, class...Unused, class F>
struct operation<R(Unused...), F>
{
F f;
static_assert(
std::is_convertible< std::result_of_t< F const&(Unused...) >, R >{}
);
template<class...Args>
R operator()( Args&&...args ) const {
return static_cast<R>(f(std::forward<Args>(args)...));
}
template<class O,
std::enable_if_t< !std::is_same<O, F>{} && std::is_convertible<O, F>{}, bool> = true
>
operation ( operation<R(Unused...), O> o ):
f(std::move(o.f))
{}
operation (F fin):
f(std::move(fin))
{}
};
template<class F=std::function<void(std::istream&, std::ostream&)>>
struct pipe:operation<void(std::istream&, std::ostream&), F> {
using operation<void(std::istream&, std::ostream&), F>::operation;
};
template<class F>
pipe (F)->pipe <F>;
template<class First, class Second>
auto operator|( pipe <First> first, pipe <Second> second )
{
return pipe {[=](auto& in, auto& out){
std::stringstream intermediate;
first( in, intermediate );
second( intermediate, out );
}};
}
template<class F=std::function< void(std::function< void(std::ostream&)>) >>
struct source:operation<void(std::function< void(std::istream&)>), F> {
using operation<void(std::function< void(std::istream&)>), F>::operation;
};
template<class F>
source(F)->source<F>;
template<class F=std::function< void(std::function< void(std::ostream&)>) >>
struct sink:operation<void(std::function< void(std::ostream&)>), F> {
using operation<void(std::function< void(std::ostream&)>), F>::operation;
};
template<class F>
sink(F)->sink<F>;
template<class First, class Second>
auto operator|( source<First> src, pipe<Second> p ) {
return source{[=]( auto&& f ){
src([&](auto&& in){
std::stringstream ss;
p( in, ss );
f( ss );
});
}};
}
template<class First, class Second>
auto operator|( pipe<First> p, sink<Second> snk ) {
return sink{[=]( auto&& f ){
snk([&](auto&& out){
std::stringstream ss;
f(ss);
p(ss, out);
});
}};
}
void copy_f( std::istream& is, std::ostream& os ) {
char c;
while (is.get(c)) {
os << c;
}
}
inline pipe copy{copy_f};
template<class First, class Second>
void operator|( source<First> src, sink<Second> snk ) {
src([&](auto&& in){
snk([&](auto&& out){
copy( in, out );
});
});
}
}
you can then do:
using namespace stream;
auto src = source{[](auto&& f){
std::stringstream ss;
ss << "Hello world\n";
f(ss);
}};
auto snk = sink{[](auto&& f){
f(std::cout);
}};
src|copy|copy|copy|snk;
Live example
A source is a function object that in turn takes a function object, that it passes an istream& to.
A sink is a function object that in turn takes a function object, that it passes a ostream& to.
This double-function syntax deals with annoying lifetime issues, and lets you do cleanup before/after the client stream-user does stuff with the stream.
And a slightly more insane version that supports direct piping to/from streams is here.

Make struct behave like std::tuple

I've written some generic code which manages a list of tuples. Now I want to use that code, but instead of std::tuple I would like to use simple structs, so I can access the variables using names instead of indicies. Is there an easy way to make these structs behave like std::tuple, so I can use it with my generic code?
struct foo {
int x;
float y;
// some code to enable tuple like behavior (e.g. std::get, std::tuple_size)
};
I've tried adding a as_tuple member function which returns all members using std::tie. This works but requires to call this member function at all places where I need the tuple behavior.
The manual way:
struct foo {
int x;
float y;
};
namespace std
{
template <>
class tuple_element<0, foo> {
using type = int;
};
template <>
class tuple_element<1, foo> {
using type = float;
};
template <std::size_t I>
tuple_element_t<I, foo>& get(foo&);
template <>
tuple_element_t<0, foo>& get(foo& f) { return f.x;}
template <>
tuple_element_t<1, foo>& get(foo& f) { return f.y; }
template <std::size_t I>
tuple_element_t<I, foo> get(const foo&);
template <>
tuple_element_t<0, foo> get(const foo& f) { return f.x;}
template <>
tuple_element_t<1, foo> get(const foo& f) { return f.y; }
}
An other way is to write functions as_tuple:
template <typename ... Ts>
std::tuple<Ts...>& as_tuple(std::tuple<Ts...>& tuple) { return tuple; }
std::tuple<int&, float&> as_tuple(foo& f) { return std::tie(f.x, f.y); }
and wrap your call before using tuple-like.
First, as_tuple should be a free function in the namespace of the class. This lets you extend types other people write.
Next, you should attempt to call get in an ADL-enabled context.
using std::get;
auto& x = get<1>(foo);
if you do that we can pull off some magic.
struct get_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend decltype(auto) get( T&& t ) {
return std::get<I>( as_tuple( std::forward<T>(t) ) );
}
};
now
struct foo:get_from_as_tuple {
int x;
float y;
friend auto as_tuple( get_from_as_tuple const& self ) {
return std::tie( self.x, self.y );
}
};
we can do this:
foo f;
using std::get;
std::cout << get<0>(f) << "," << get<1>(f) << "\n";
Now, this still doesn't enable tuple_size and tuple_element.
There is no trivial way to do that part, but we can work around it.
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
namespace tup {
namespace adl_get {
using std::get;
template<std::size_t I,
class T
>
auto get_helper( T&& t )
RETURNS( get<I>(std::forward<T>(t) ) )
}
template<std::size_t I, class T>
auto get( T&& t )
RETURNS(adl_get::get_helper<I>(std::forward<T>(t)))
}
now tup::get<7>( x ) will dispatch to either std::get or another get in x's namespace based off of overload resolution rules.
We can create similar helpers:
namespace util {
template<class T>
struct tag_t {constexpr tag_t(){}};
template<class T>
constexpr tag_t<T> tag{};
}
namespace tup {
namespace adl_tuple_size {
template<class T>
constexpr std::size_t get_tuple_size( tag_t<T>, ... ) {
return std::tuple_size<T>::value;
}
template<class T>
constexpr auto get_tuple_size( tag_t<T>, int )
RETURNS( tuple_size( tag_t<T> ) )
}
template<class T>
constexpr std::size_t tuple_size() {
return adl_tuple_size::get_tuple_size( tag<T> );
}
}
now tup::tuple_size<Foo>() is a constexpr call that gets the size of Foo by either (A) invoking tuple_size( tag_t<Foo> ) in an ADL-enabled context, or (B) returning std::tuple_size<Foo>::value.
Once we have this we can create another helper base type:
struct tuple_size_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend std::size_t tuple_size( T&& t ) {
return std::tuple_size< decltype(as_tuple( std::forward<T>(t) ) ) >::value;
}
};
struct as_tuple_helpers : get_from_as_tuple, tuple_size_from_as_tuple {};
struct foo:as_tuple_helpers {
// ..
};
and we now have 2 primitives.
Repeat this for tag_t<E&> tuple_element( tag_t<T> ). Then we can write a tup::tuple_element<T, 0> alias that dispatches as you like it.
Finally, adapt your existing code that works with std:: tuple facilities to use tup:: facilities. It should work with existing tuple code, and will also work with types inherited from as_tuple_helper which has a friend as_tuple defined.
This does not, however, give you support for structured bindings.

Type agnostic getter methods

I'm writing a client for a system that returns values of natural types in random order (some can be int, others float, others string [well, almost natural]). The problem is, I don't know what type a value will be at compile time.
Since I don't know the type of the value to be returned until after the remote system has been queried, what is the best way to provide a uniform interface that allows a user of the client library to extract the value in the right type?
If querying the remote system once returns a string, I'd like my get_value() to return a string. If an int, make it return an int. Alternatively, how to have the client library call the getter with the right type?
I guess templates with type hinting would be a good way to achieve this?
Examine boost or std variant if there is a finite list of supported types.
If not a finite list, boost or std any (or a variant containing an any).
You can find other implementations as well. The std versions are in C++17.
A simplified version of variant could probably be written in a 100 or two lines of code.
Here is a crude C++14 variant:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
Live example.
Converting to C++11 will consist of a bunch of replacing lambdas with helpers. I don't like writing in C++11, and this C++14 is a mostly mechanical transformations away from it.
It is crude, in that visit takes exactly one variant and returns void, among other reasons.
Code is almost completely untested, but the design is sound.
There are two different use case. If the client program can know in advance the type of the value it wants, you can either use a different getter for each possible type (the good old C way with for example getInt, getDouble, getString), or use templated getters (modern C++ way):
template <class T>
T get(char *byte_array) {
T value;
# manage to extract the value
return T;
}
and explictely instanciate them to make sure that they will be available.
In the client library, the usage will be:
int i = get<int>(byte_array);
If the client program may received data in an order which is unknow at compile time, you must find a way to return a variant data type (old Basic programmers remember that). You can find implementations in boost or C++ 17, but a trivial implementation could be:
struct variant {
enum Type { INT, DOUBLE, STRING, ... } type;
union {
int int_val;
double d_val;
std::string str_val;
...
};
};
In that case the client program will use
variant v = get(byte_array);
switch v.type {
case INT:
...
}
I had this exact same problem with the HDF5 library. The type of a dataset from a file can be any native type (ignoring structs for now). My solution was the following:
Create an abstract base class
Create a template class that derives from the abstract class, where the type is the runtime type you need
Create static methods in the base class that will read the type from your system, and then decide what to instantiate.
For example:
static std::shared_ptr<Base> GetVariable()
{
switch(mytype)
{
case INT16:
return std::make_shared<Derived<uint16_t>>(value);
case INT32:
return std::make_shared<Derived<uint32_t>>(value);
//etc...
}
}
There are many advantages of this, including that you could make a base-class method that gets the string value for all your types, and use the cool std::to_string for all types. You'll only need specializations if you need to do something that is type specific.
You said you were working in C++11 so if you don't want to use Boost for it's Variant type then you can use a standard C-Style union if the return type is a limited set of types.
If you want a variable, unrestricted, return type then you will probably want to look into 'Concept Based Polymorphism' or 'Type Erasure' design patters.
It's also worth looking into 'Template Specialisation', it won't be any use unless you know the return type when calling but it's a good trick to get specific type handlers with the same signature.

Adapting a function that returns std::future<T> to std::future<U>

Suppose I have an asynchronous functional map primitive which takes a std::vector as input and returns a std::future to a Container of my choice as output:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
I'd like to build an analogous async_for_each function by adapting async_map:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f);
The problem is that async_for_each returns std::future<void>, while async_map returns std::future<Container>, and void is not a Container.
I can get something close to what I want by constructing a type which fulfills the Container requirements but ignores assignments to it (empty_container in my initial attempt), but a std::future of this type is still not std::future<void>.
I have the following constraints on my solution:
There must be only one implementation of async_map, with the given function signature (i.e., no async_map<void> specialization)
There must be only one std::future created (i.e., no .then()-style continuation)
I was hoping there is an efficient way to convert between std::futures of related types (or cast a std::future<T> to std::future<void>), but the answer to this question suggests it is not possible.
Random ideas:
Can async_for_each wrap its function in a clever way to solve this problem?
Can the type used for Container act like void in async_for_each, but act like Container in async_map?
My initial attempt is below. Is it possible to build what I want given these constraints?
#include <future>
#include <vector>
#include <iostream>
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return std::async([=]
{
Container result(in.size());
for(size_t i = 0; i < in.size(); ++i)
{
result[i] = f(in[i]);
}
return result;
});
}
struct empty_container
{
empty_container(size_t) {}
struct empty
{
template<class T>
empty operator=(const T&) const { return empty(); }
};
empty operator[](size_t) { return empty(); }
};
template<class Function>
struct invoke_and_ignore_result
{
Function f;
template<class T>
empty_container::empty operator()(T&& x) const
{
f(std::forward<T>(x));
return empty_container::empty();
}
};
template<class T, class Function>
//std::future<void> async_for_each(const std::vector<T>& in, Function f)
std::future<empty_container> async_for_each(const std::vector<T>& in, Function f)
{
invoke_and_ignore_result<Function> g{f};
std::future<empty_container> f1 = async_map<empty_container>(in, g);
return f1;
}
int main()
{
std::vector<int> vec(5, 13);
async_for_each(vec, [](int x)
{
std::cout << x << " ";
}).wait();
std::cout << std::endl;
return 0;
}
I think you are using the wrong primitive.
Here I build everything up with a different primitive -- a sink.
A sink can consume data via operator()(T&&)&. It then returns some result via operator()()&&.
Here is a async_sink function:
template<class Container, class Sink>
std::future<std::result_of_t<std::decay_t<Sink>()>>
async_sink(Container&& c, Sink&& sink)
{
return std::async(
[c=std::forward<Container>(c), sink=std::forward<Sink>(sink)]
{
for( auto&& x : std::move(c) ) {
sink( x );
}
return std::move(sink)();
});
}
Here is an implementation of a sink that puts things into a container, then returns it:
template<class C>
struct container_sink_t {
C c;
template<class T>
void operator()( T&& t ){
c.emplace_back( std::forward<T>(t) );
}
C operator()()&&{
return std::move(c);
}
};
Here is a sink that takes a function and a sink and composes them:
template<class F, class S>
struct compose_sink_t {
F f;
S s;
template<class T>
void operator()(T&& t){
s(
f(std::forward<T>(t))
);
}
std::result_of_t<S()> operator()()&&{
return std::move(s)();
}
};
template<class C, class F>
compose_sink_t<std::decay_t<F>, container_sink_t<C>>
transform_then_container_sink( F&& f ) {
return {std::forward<F>(f)};
}
Here is a sink that takes a function, calls it, and returns void:
template<class F>
struct void_sink_t {
F f;
template<class T>
void operator()(T&& t)
{
f(std::forward<T>(t));
}
void operator()() {}
};
template<class F>
void_sink_t<std::decay_t<F>> void_sink(F&&f){return {std::forward<F>(f)}; }
now your map is:
template<class Container, class T, class Function>
std::future<Container> async_map(const std::vector<T>& in, Function f)
{
return async_sink(
in,
transform_then_container_sink<Container>(std::forward<F>(f))
);
}
and your for_each is:
template<class T, class Function>
std::future<void> async_for_each(const std::vector<T>& in, Function f)
{
return async_sink(
in,
void_sink(std::forward<F>(f))
);
}
I freely use C++14 features, because they made the code better. You can replace the move-into-container with a copy for a touch less efficiency, and write your own _t aliases.
The above code has not been tested or run, so there are probably bugs in it. There is one issue I'm uncertain of -- can a lambda returning void end with a return void_func() in that context? -- but as that uglyness is in one spot, it can be worked around even if it doesn't work.

How to convert std::future<T> to std::future<void>?

I have a situation where I have a std::future<some_type> resulting from a call to API A, but need to supply API B with a std::future<void>:
std::future<some_type> api_a();
void api_b(std::future<void>& depend_on_this_event);
In the absence of proposed functionality such as .then() or when_all(), is there any efficient way to throw away the value attached to a std::future<T> and be left only with the underlying std::future<void> representing the event's completion?
Something like the following could work but would be potentially inefficient:
auto f = api_a();
f.wait();
auto void_f = std::async(std::launch::defer, []{});
api_b(void_f);
The best you can get is probably this:
auto f = api_a();
auto void_f = std::async(std::launch::deferred,[fut = std::move(f)]{ fut.wait();});
api_b(void_f);
template<class U>
struct convert_future_t {
template<class T>
std::future<U> operator()( std::future<T>&& f ) const {
return std::async(std::launch::deferred,
[f=std::move(f)]()->U{ return f.get(); }
);
}
}
template<>
struct convert_future_t<void> {
template<class T>
std::future<void> operator()( std::future<T>&& f ) const {
return std::async(std::launch::deferred,
[f=std::move(f)]()->void{ f.get(); }
);
}
}
template<class U, class T>
std::future<U> convert_future( std::future<T>&& f ) {
return convert_future_t<U>{}(std::move(f));
}
this is a generic version of #sbabbi's answer.
api_b( convert_future<void>( api_a() ) );
that allows for any target and dest type to work transparently.
The large downside to this approach is that the resulting future is a deferred future wrapping a (possibly async) future, which means that .wait_for() and .ready() APIs do not work like async futures do. The returned future will never be ready until waited.
So we can improve this marginally:
template<class T>
struct ready_future_t {
template<class...Us>
std::future<T> operator()( Us&&...us ) const {
std::promise<T> p;
p.set_value(T(std::forward<Us>(us)...));
return p.get_future();
}
};
template<>
struct ready_future_t<void> {
using T=void;
// throws away the Us&&...s
template<class...Us>
std::future<T> operator()( Us&&...us ) const {
std::promise<T> p;
p.set_value();
return p.get_future();
}
};
template<class T, class...Us>
std::future<T> ready_future(Us&&...us){
return ready_future_t<T>{}(std::forward<Us>(us)...);
}
template<class U>
struct convert_future_t {
template<class T>
std::future<U> operator()( std::future<T>&& f ) const {
if (f.wait_for(0ms)==std::future_status::ready)
return ready_future<U>(f.get());
return std::async(std::launch::deferred,
[f=std::move(f)]()->U{ return f.get(); }
);
}
};
template<>
struct convert_future_t<void> {
template<class T>
std::future<void> operator()( std::future<T>&& f ) const {
if (f.wait_for(0ms)==std::future_status::ready)
return ready_future<void>();
return std::async(std::launch::deferred,
[f=std::move(f)]()->void{ f.get(); }
);
}
};
where at least if the future was already ready by the time we converted it, the returned future is also ready.
live example