I'd like to replace boost::variants with C++17 std::variant and get rid of boost::recursive_wrapper, to remove dependency on boost completely in following code. How may I do that?
#include <boost/variant.hpp>
#include <type_traits>
using v = boost::variant<int, boost::recursive_wrapper<struct s> >;
struct s
{
v val;
};
template<template <typename...> class R, typename T, typename ... Ts>
auto reduce(T t, Ts ... /*ts*/)
{
return R<T, Ts...>{t};
}
template<typename T, typename F>
T adapt(F f)
{
static_assert(std::is_convertible_v<F, T>, "");
return f;
}
int main()
{
int val1 = 42;
s val2;
auto val3 = adapt<v>(reduce<boost::variant>(val1, val2));
}
There are two generic functions: first function reduce chooses at runtime which argument to return (here it just returns first argument for brevity), second function adapt converts a value of type F to a value of type T.
In this example reduce returns an object of type boost::variant<int, s> which is then converted to an object of type boost::variant<int, boost::recursive_wrapper<s> >.
boost::variant will heap allocate in order to have part of itself be recursively defined as itself. (It will also heap allocate in a number of other situations, uncertain how many)
std::variant will not. std::variant refuses to heap allocate.
There is no way to actually have a structure containing a possible variant of itself without a dynamic allocation, as such a structure can easily be shown to be infinite in size if statically declared. (You can encode the integer N by having N recursions of not-the-same: no fixed size buffer can hold an infinite amount of information.)
As such, the equivalent std::variant stores a smart pointer of some kind placeholder of a recursive instance of itself.
This may work:
struct s;
using v = std::variant< int, std::unique_ptr<s> >;
struct s
{
v val;
~s();
};
inline s::~s() = default;
and failing that, try:
struct destroy_s;
struct s;
using v = std::variant<int, std::unique_ptr<s, destroy_s> >;
struct s
{
v val;
~s();
};
struct destroy_s {
void operator()(s* ptr){ delete ptr; }
};
inline s::~s() = default;
It does mean that client code has to knowingly interact with the unique_ptr<s> and not the struct s directly.
If you want to support copy semantics, you'll have to write a value_ptr that does copies, and give it the equivalent of struct copy_s; to implement that copy.
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
new(dest) T(*tin);
}
};
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
using copier_type=Copier;
// also typedefs from unique_ptr
using Base::Base;
value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( Base b, Copier c={} ):
Base(std::move(b)),
Copier(std::move(c))
{}
Copier const& get_copier() const {
return *this;
}
value_ptr clone() const {
return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {
**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}
Live example of value_ptr.
Related
I'm trying to pack my object to 64 bytes (homegrown properties), and I need to save getter and setter member functions.
I really like std::function but is way big:
sizeof(std::function<int(void)>) gives 40 bytes (VS2017, x64).
I'm trying to figure out is there any thin version of std::function 16 bytes large and without extra allocations inside?
Update: Solution looks like this, it takes 16 bytes (x64) for two member functions.
Member function pointers became template argument so they take nothing, stored just object pointer and vptr.
Thanks to #Yakk.
class Property
{
public:
virtual ~Property() {}
virtual QVariant value() const = 0;
virtual void setValue(const QVariant& value) = 0;
};
template<typename TYPE,
typename CLASS,
TYPE(CLASS::*get)() const,
void(CLASS::*set)(const TYPE &)>
class PropertyValue : public Property
{
public:
PropertyValueGet() = delete;
PropertyValueGet(PropertyValueGet const& ) = delete;
PropertyValueGet& operator=(PropertyValueGet const& ) = delete;
PropertyValueGet(CLASS* object) : m_object(object) {}
TYPE getValue() const {
return (m_object->*get)();
}
void setValue(const TYPE& value) {
(m_object->*set)(value);
}
// Property implementation
//
virtual QVariant value() const final {
QVariant v = QVariant::fromValue<TYPE>(getValue());
return v;
}
virtual void setValue(const QVariant& value) final {
setValue(value.value<TYPE>());
}
private:
CLASS* m_object = nullptr;
};
The type of m_getter is decltype(m_getter).
Using std::mem_fn in Modern C++ is a bad idea - lambda expressions are a cleaner and more transparent (both to the developer and the compiler) way of binding arguments.
Stephan T. Lavavej, in the talk “functional: What's New, And Proper Usage", mentions some drawbacks of std::bind and std::mem_fn compared to lambdas:
You should use a lambda expression instead. E.g.:
auto my_getter = [](Foo& foo){ return foo.getter(); };
Foo some_foo;
my_getter(some_foo);
or
Foo some_foo;
auto my_getter = [&some_foo](){ return some_foo.getter(); };
my_getter();
&Foo::getter has a type, it is TYPE(Foo::*)(void), which is most likely a smaller type than std::function<TYPE(void)>. Similarly &Foo::setter is a void(Foo::*)(TYPE).
But you can get even smaller, by discarding the errant notion that getters and setters are encapsulation, with &Foo::field, which is a TYPE Foo::*.
template<typename T, typename C>
struct Property
{
Property(T C::*member) : member(member) {}
T & get(C & c) { return std::invoke(member, c); }
void set(C & c, T & t) { std::invoke(member, c) = t; }
private:
T C::* member;
}
Alternately accompanying an instance
template<typename T, typename C>
struct Property
{
Property(T C::*member, C & instance) : member(member), instance(instance) {}
T & get() { return std::invoke(member, instance); }
void set(T & value) { std::invoke(member, instance) = value; }
private:
T C::* member;
C & instance;
}
The OP has clarified that the members used are fixed:
template<class T>
struct generic_property {
virtual T getValue() const = 0;
virtual void setValue(T const&) = 0;
protected:
virtual ~generic_property() {}
};
template<class D, class T, void(D::*set)(T const&), T(D::*get)() const>
struct property:generic_property<T> {
T getValue() const final {
return (self->*get)();
}
void setValue(T const& t) final {
(self->*set)(t);
}
property( D* s ):self(s) {}
// cannot usually safely copy/move/trivial:
property() = delete;
property( property const& ) = delete;
property& operator=( property const& ) = delete;
private:
D* self = 0;
};
struct Bob {
void setFoo( int const& i ) { std::cout << i << " set\n"; }
int getFoo() const { std::cout << 42 << " get\n"; return 42; }
property<Bob, int, &Bob::setFoo, &Bob::getFoo> foo;
Bob():foo(this) {}
};
each property takes up 1 pointers worth of space (so 32 to 64 bits, or 4-8 bytes, depending on the bit-width of the OS).
Adding virtual and generic_property<T> makes it take up another pointer's worth of space.
live example.
We could type erase manually in generic_property instead of by virtual inheritance.
This is a c++17 answer, because doing this in c++11 is really annoying.
Here is a "simple" stateless view-only std::function.
// utility tag type for dispatching
template<class Tag>
struct tag_t {};
// helper type to find the "caller" function pointer used to erase invoke:
template<class Sig>
struct caller_type;
template<class R, class...Args>
struct caller_type<R(Args...)> {
using type = R(*)(void*, Args&&...);
};
template<class Sig>
using caller = typename caller_type<Sig>::type;
// make a caller<Sig> that type erases calling T with Sig:
template<class T, class Sig>
struct make_caller;
template<class T, class R, class...Args>
struct make_caller<T, R(Args...)> {
caller<R(Args...)> operator()() const {
return [](void* ptr, Args&&...args)->R {
return (*static_cast<T*>(ptr))( std::forward<Args>(args)... );
};
}
};
template<class T, class...Args>
struct make_caller<T, void(Args...)> {
caller<void(Args...)> operator()() const {
return [](void* ptr, Args&&...args)->void {
(*static_cast<T*>(ptr))( std::forward<Args>(args)... );
};
}
};
// provides operator() overload compatible with Sig,
// then dispatches the call down through a derived type D:
template<class D, class Sig>
struct call_dispatch;
template<class D, class R, class...Args>
struct call_dispatch<D, R(Args...)> {
R operator()(Args...args)const {\
auto* caller = self()->get_caller(tag_t<R(Args...)>{});
return (*caller)( self()->pvoid(), std::forward<Args>(args)... );
}
auto self() { return static_cast<D*>(this); }
auto self() const { return static_cast<D const*>(this); }
};
// stores a function pointer to invoke Sig
template<class Sig>
struct call_storage {
caller<Sig> f = nullptr;
template<class T>
static call_storage make() {
return {make_caller<T, Sig>{}()};
}
caller<Sig> get_caller( tag_t<Sig> ) const { return f; }
};
// a table of such function pointers
template<class...Sig>
struct call_vtable:call_storage<Sig>... {
template<class T>
static call_vtable make() {
return {call_storage<Sig>::template make<T>()...};
}
using call_storage<Sig>::get_caller...;
};
// overload helper to dispatch to correct Sig:
template<class D, class...Sig>
struct call_dispatcher:
call_dispatch<D, Sig>...
{
using call_dispatch< D, Sig >::operator()...;
};
// Erases invoking but not storing an arbitrary type T
// with all of ...Sig. Stores the invokers inside itself,
// not in an external vtable, to increase locality at the cost
// of per-instance size:
template<class... Sig>
struct call_view_t:
private call_vtable<Sig...>,
call_dispatcher< call_view_t<Sig...>, Sig... >
{
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, call_view_t>{}, bool> =true
>
call_view_t( F&& f ):
call_vtable<Sig...>( call_vtable<Sig...>::template make<std::decay_t<F>>() ),
ptr( std::addressof(f) )
{}
call_view_t()=default;
call_view_t(call_view_t const&)=default;
call_view_t& operator=(call_view_t const&)=default;
explicit operator bool() const { return ptr != nullptr; }
void* pvoid() const { return ptr; }
using call_vtable<Sig...>::get_caller;
private:
void* ptr = 0;
};
it requires 2 pointers, one to store non-owning state and one to store an invoker (it also supports any number of signatures on the thing to invoke).
We can create a new call_view type that instead of storing a ptr*, stores a block of memory; in that block, it placement new's the state of the F passed in and pvoid() returns a pointer to it.
Unless you restrict yourself to trivial destruction and copy/move, you'll also have to store pointers to functions on how to do that.
As call_impl only varies based on the type of thing stored and not its value, we can store the f's in a vtable and only inherit from call_dispatch. Inside that vtable we can store copy/move/destroy of our copy of F.
This permits us to have a trim std::function with state that only requires 1 pointer overhead over the storage of whatever state you want to put in the std::function.
Note that the above code probably has typos, and doesn't solve your problem. It has 9/10 of the pieces to solve your problem.
// a vtable that represents "I can be copied or moved and destroyed":
struct copyable_vtable {
void(*dtor)(void*)=0;
void(*copy)(void* dest, void const* src)=0;
void(*move)(void* dest, void* src)=0;
template<class T>
static copyable_vtable make() {
return {
[](void* ptr){ static_cast<T*>(ptr)->~T(); },
[](void* dest, void const* src){
::new( dest ) T(*static_cast<T const*>(src));
},
[](void* dest, void * src){
::new( dest ) T(std::move(*static_cast<T const*>(src)));
}
};
}
};
// the vtable that our "small function" needs:
template<class...Sig>
struct small_func_vtable:
copyable_vtable,
call_vtable<Sig...>
{
template<class T>
static small_func_vtable make() {
return {
copyable_vtable::template make<T>(),
call_vtable<Sig...>::template make<T>()
};
}
template<class T>
static small_func_vtable const* get() {
static const auto vtable = make<T>();
return &vtable;
}
};
// bundles up the size and alignment requirements:
template<std::size_t S=sizeof(void*)*3, std::size_t A=alignof(void*)>
struct size_params {
enum { size = S, align = A };
};
// A small stack allocated std::function that refuses to get bigger
// If you try to construct it with something larger, you get
// a static assert failure. Also supports arbitrary number of
// overloads of ():
template<class Size, class...Sig>
struct small_function:
call_dispatcher< small_function<Size, Sig...>, Sig... >
{
private:
small_func_vtable<Sig...> const* vtable = 0;
mutable std::aligned_storage_t< Size::size, Size::align > data;
public:
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, small_function>{}, bool> =true
>
small_function( F&& f )
{
static_assert( sizeof(std::decay_t<F>)<=Size::size );
static_assert( alignof(std::decay_t<F>)<=Size::align );
::new( (void*)&data ) std::decay_t<F>( std::forward<F>(f) );
vtable = small_func_vtable<Sig...>::template get<std::decay_t<F>>();
}
small_function()=default;
// we could refactor this into base class:
small_function(small_function const& o) {
if (!o.vtable) return;
o.vtable->copy( pvoid(), o.pvoid() );
vtable = o.vtable;
}
small_function(small_function&& o) {
if (!o.vtable) return;
o.vtable->move( pvoid(), o.pvoid() );
vtable = o.vtable;
}
small_function& operator=(small_function const& o) {
if (this == &o) return *this;
if (vtable) {
vtable->dtor(pvoid());
vtable = nullptr;
}
if (o.vtable) {
o.vtable->copy( pvoid(), o.pvoid() );
vtable = o.vtable;
}
return *this;
}
small_function& operator=(small_function&& o) {
if (this == &o) return *this;
if (vtable) {
vtable->dtor(pvoid());
vtable = nullptr;
}
if (o.vtable) {
o.vtable->move( pvoid(), o.pvoid() );
vtable = o.vtable;
}
return *this;
}
// use null vtable to determine if we are empty:
explicit operator bool() const { return vtable != nullptr; }
// these must be visible to dispatch system. I think
// they are harmless to expose to end users, so I don't bother
// with making them private and friending dispatch system:
void* pvoid() const { return &data; }
template<class S>
caller<S> get_caller(tag_t<S> t) const {
if (!vtable) return nullptr;
return vtable->get_caller(t);
}
};
template<class...Sig>
using simple_small_function = small_function< size_params<>, Sig... >;
and try simple_small_function.
Live example.
Let's look at what we need for std::function:
A pointer for the list of how to copy, destroy, and invoke the payload. Knowledge of the targets type and how to retrieve it is a bonus, and extra cost per type is marginal (none per instance).
The target. To avoid rampant allocating, there should be space for at least a member-function-pointer. Unfortunately, the conforming format MSVC uses is a whomping 4 pointers big (they use different smaller non-conforming ones in different situations). Other ABIs use smaller ones for full effect.
So, that's 5 pointers worth => 5*8 = 40 Bytes on 64 bit Windows as a minimum to accomodate member-function-pointers.
How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
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));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.
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.
class BaseClass{
public:
std::string name;
BaseClass(std::string typeName) : name(typeName) {};
std::string GetType(){ return name; }
};
template<typename T>
class DerivedClass : public BaseClass{
public:
T val;
DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
};
template<typename U, typename L>
void foo1(U & arg1, L & arg2)
{
std::cout << arg1.val + arg2.val << std::endl;
}
void foo(BaseClass *arg1, BaseClass *arg2)
{
if(arg1->GetType() == "Int")
{
auto p1 = (DerivedClass<int>*)arg1;
if(arg2->GetType() == "Int")
{
auto p2 = (DerivedClass<int>*)arg2;
foo1(*p1, *p2);
}
else if(arg2->GetType() == "Float")
{
auto p2 = (DerivedClass<float>*)arg2;
foo1(*p1, *p2);
}
//else if () AND SO ON ........
}
else if(arg1->GetType() == "Float")
{
auto p1 = (DerivedClass<float>*)arg1;
if(arg2->GetType() == "Int")
{
auto p2 = (DerivedClass<int>*)arg2;
foo1(*p1, *p2);
}
else if(arg2->GetType() == "Float")
{
auto p2 = (DerivedClass<float>*)arg2;
foo1(*p1, *p2);
}
}
//else if () AND SO ON .....
}
int main()
{
BaseClass *k1 = new DerivedClass<int>("Int", 2);
BaseClass *k2 = new DerivedClass<float>("Float", 4.32);
foo(k1, k2);
return 0;
}
I have some similar problem as in test case above.
In function foo, is there any more elegant way of parsing multiple types to ladder of ifs to run template function?
Ladder of if for 1 or 2 arguments is not that bad, but it goes (arguments count)^(types count);
You can use some template magic to generate the if...else chains for you. Firstly, write a generic compile-time iteration function:
template <typename TF, typename... Ts>
void for_each_arg(TF&& f, Ts&&... xs)
{
return (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}
You will also need something that binds a type to a string:
template <typename T>
struct bound_type
{
using type = T;
std::string _name;
bound_type(std::string name) : _name{std::move(name)} { }
};
Then you can use it to check the types that you're interested in:
void foo(BaseClass *arg1, BaseClass *arg2)
{
const auto for_bound_types = [](auto&& f)
{
return for_each_arg(std::forward<decltype(f)>(f),
bound_type<int>{"Int"},
bound_type<float>{"Float"},
bound_type<double>{"Double"});
};
for_bound_types([&](const auto& t1)
{
if(arg1->GetType() != t1._name) return;
for_bound_types([&](const auto& t2)
{
if(arg2->GetType() != t2._name) return;
using t1_type = typename std::decay_t<decltype(t1)>::type;
using t2_type = typename std::decay_t<decltype(t2)>::type;
auto& p1 = static_cast<DerivedClass<t1_type>&>(*arg1);
auto& p2 = static_cast<DerivedClass<t2_type>&>(*arg2);
foo1(p1, p2);
});
});
}
live wandbox example
I would like to avoid using std::string here, but compile-time strings are unreasonably hard in C++. Something like typestring could be used instead.
In C++17, for_each_arg would be redundant thanks to fold expressions. std::apply could also be used to implement for_bound_types.
const auto for_bound_types = [](auto&& f)
{
return std::apply([&](auto... xs){ (f(xs), ...); },
std::make_tuple(bound_type<int>{"Int"},
bound_type<float>{"Float"},
bound_type<double>{"Double"}));
};
The code generated will be args^types, but you can get the compiler to do it for you.
Get ahold of std::variant or boost::variant or write it yourself.
Have each argument give you either a variant with a copy, or a variant with a pointer-to the element or itself.
Use std::visit or boost::apply_visitor on these variants.
template<typename T>
class DerivedClass;
class BaseClass{
public:
std::string name;
virtual std::variant< DerivedClass<int>*, DerivedClass<double>* >
self() = 0;
BaseClass(std::string typeName) : name(typeName) {};
std::string GetType(){ return name; }
};
template<typename T>
class DerivedClass : public BaseClass{
public:
T val;
DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
std::variant< DerivedClass<int>*, DerivedClass<double>* >
self() overload { return this; }
std::variant< DerivedClass<int> const*, DerivedClass<double> const* >
self() const overload { return this; }
};
Now we get:
void foo(BaseClass *arg1, BaseClass *arg2)
{
auto a1 = arg1->self();
auto a2 = arg2->self();
auto foo_overloads = [](auto&&...args)->decltype(auto){ return foo(decltype(args)(args)...); };
std::visit( foo_overloads, a1, a2 );
}
Now this punts the problem to "how do I write std::visit and std::variant". But code for both is available on the internet. And both are available as std::experimental::variant and std::experimental::visit.
Maintaining the list of types int double can be done in a type list, and the list variant<DerivedClass<int>*, DerivedClass<double>*> generated from it.
If you want the list to be bespoke and dynamic at runtime cost, you could instead take a type list, build a map from std::string to std::function<variant< DerivedClass<Ts>*... >( BaseClass* )> at the point of use (listing the types you support there), and do the same thing there.
I'm trying to use std::mem_fun_ref (Yes, the deprecated
version. Reasons below) to call a member function through a proxy.
template<typename T>
struct proxy {
T& operator*() { return *t; }
T* operator->() { return t; }
// no address of etc
T* t;
};
struct A {void foo() {}};
int main()
{
A a;
proxy<A> pa = {&a};
std::mem_fun_ref_t<void, A>
fn = std::mem_fun_ref(&A::foo);
fn(pa); // borks
return 0;
}
This works well with C++11 std::mem_fn but not boost::mem_fn, but
I can use neither of those, as I need to specify the type of the
binder in another place and the type of the resulting binder is
unspecified for boost::mem_fn. This wouldn't be a problem if I could
use decltype but I can't as the code needs to be compatible with
C++03.
What is the easiest way to work around this? A custom
mem_fun_through_proxy?
Edit: Another caveat is that the proxy class cannot be changed.
As Georg recommended, I implemented my own solution. Here is the short version:
// snipped solution: does not include const version and hack for void
// returns
#include <functional>
namespace mine {
template<typename Ret, typename T>
class mem_fun_ref_t : public std::unary_function<T, Ret>
{
public:
explicit
mem_fun_ref_t(Ret (T::*f)())
: f(f) { }
template<typename U>
Ret
operator()(U& u) const
{ return (*u.*f)(); }
Ret
operator()(T& t) const
{ return (t.*f)(); }
private:
Ret (T::*f)();
};
} // mine
template<typename T>
struct proxy {
T& operator*() { return *t; }
T* operator->() { return t; }
// no address of etc
T* t;
};
struct X {
int foo() {return 23;}
};
int main()
{
mine::mem_fun_ref_t<int, X> fn(&X::foo);
X x;
// normal
fn(x);
proxy<X> px = {&x};
fn(px);
return 0;
}