So in C++ there is now make_from_tuple as:
T obj = std::make_from_tuple<T>( { Args... args } ); // args represents a tuple
but how would one do:
T* obj = std::make_new_from_tuple<T*>( { Args... args } );
There is make_shared and make_unique but neither of those takes a tuple (and I'm not sure how one would extract the arguments from the tuple if that is the direction to go, as you can always make_unique then release if you want the raw pointer).
Very simple example 1:
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
For a more complex example, if the tuple contains a unique_ptr you want to forward, I will want that to work too.
You can basically copy the implementation from cppreference's Possible implementation, chuck in a make_unique and it just works:
#include <string>
#include <memory>
namespace detail {
template<class T, class Tuple, std::size_t... I>
constexpr std::unique_ptr<T> make_new_from_tuple_impl(Tuple&& t, std::index_sequence<I...>)
{
static_assert(std::is_constructible_v<T,
decltype(std::get<I>(std::declval<Tuple>()))...>);
#if __cpp_lib_reference_from_temporary >= 202202L
if constexpr (std::tuple_size_v<std::remove_reference_t<Tuple>> == 1) {
using tuple_first_t = decltype(std::get<0>(std::declval<Tuple>()));
static_assert(!std::reference_constructs_from_temporary_v<T, tuple_first_t>);
}
#endif
return std::make_unique<T>(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template<class T, class Tuple>
constexpr std::unique_ptr<T> make_new_from_tuple(Tuple&& t)
{
return detail::make_new_from_tuple_impl<T>(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main()
{
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
auto a = make_new_from_tuple<A>(std::move(aTuple));
}
Or with raw pointers:
#include <string>
#include <memory>
namespace detail {
template<class T, class Tuple, std::size_t... I>
constexpr T* make_new_from_tuple_impl(Tuple&& t, std::index_sequence<I...>)
{
static_assert(std::is_constructible_v<T,
decltype(std::get<I>(std::declval<Tuple>()))...>);
#if __cpp_lib_reference_from_temporary >= 202202L
if constexpr (std::tuple_size_v<std::remove_reference_t<Tuple>> == 1) {
using tuple_first_t = decltype(std::get<0>(std::declval<Tuple>()));
static_assert(!std::reference_constructs_from_temporary_v<T, tuple_first_t>);
}
#endif
return new T(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template<class T, class Tuple>
constexpr T* make_new_from_tuple(Tuple&& t)
{
return detail::make_new_from_tuple_impl<T>(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main()
{
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
auto a = make_new_from_tuple<A>(std::move(aTuple));
}
You can use std::apply. It unpacks arguments from a tuple and passes them to a callable. You just need to wrap the constructor.
#include <tuple>
#include <string>
struct A
{
int i_; double d_; std::string s_;
A( int i, double d, const std::string& s ) : i_(i), d_(d), s_(s) {}
};
int main() {
auto aTuple = std::make_tuple( 1, 1.5, std::string("Hello") );
A* a = std::apply( [](int i,double d,const std::string& s) { return new A(i,d,s);},aTuple);
}
Perfect forwarding omitted for the sake of brevity.
Simply call new with it:
T* obj = new auto(std::make_from_tuple<T>( { Args... args } ));
// unique ptr
std::unique_ptr obj{new auto(std::make_from_tuple<T>({ Args... args }))};
// shared ptr
std::shared_ptr obj{new auto(std::make_from_tuple<T>({ Args... args }))};
// doesn't work for make_shared unfortunately
auto obj = std::apply([](auto&&... args) {
return std::make_shared<T>(std::forward<decltype(args)>(args)...);
}, { Args... args });
Because make_from_tuple returns a prvalue, this will construct the object directly in the heap with no copies or move.
Explicit use of new and delete is considered a bad practice since C++11/C++14.
So use of std::make_unique (C++14) and std::make_shared (C++11) is recommended.
template<typename T, typename...Args>
std::shared_ptr<T> make_shared_from_tuple(const std::tuple<Args...>& t)
{
return std::apply([](auto...args){ return std::make_shared<T>(args...); }, t);
}
template<typename T, typename...Args>
std::unique_ptr<T> make_unique_from_tuple(const std::tuple<Args...>& t)
{
return std::apply([](auto...args){ return std::make_unique<T>(args...); }, t);
}
https://godbolt.org/z/6zjTqK8f1
Probably it would be nice to provide also some overloads to handle move semantics.
The simplest I can write is:
template<typename obj, typename tup>
auto unique_from_tuple(tup&& t) {
return std::apply(
[] (auto&& ... args) {
return std::make_unique<obj>
(std::forward<decltype(args)>(args)...);
},
std::forward<tup>(t)
);
};
// Create a unique pointer of type obj:
auto x =unique_from_tuple<obj>(y,z,w);
Related
I am trying to implement an std::unordered_map that returns pairs of either double, int or std::string. The keys for the map are std::strings. Below is what I have tried so far:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
// A base class for boundary class
class Boundbase {
public:
Boundbase(){};
virtual ~Boundbase(){};
};
// A different map of boundaries for each different data type
template <class dType>
class Boundary : public Boundbase {
std::pair<dType, dType> bpair;
public:
//Constructor
Boundary(const std::string &lbound,
const std::string &ubound) {
setbound(lbound, ubound);
};
//A method to set boundary pair
void setbound(const std::string &lbound,
const std::string &ubound);
// A method to get boundary pair
std::pair<dType, dType> getbound() {return bpair;}
};
// Class to hold the different boundaries
class Boundaries {
std::unordered_map<std::string, Boundbase*> bounds;
public:
//Constructor
Boundaries() {};
// A method to set boundary map
void setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb);
// A template to get boundaries.
std::unordered_map<std::string, Boundbase*> getbounds()
{return bounds;}
};
// A method to set covariate boundary
template <class dType> void
Boundary<dType>::setbound(const std::string &lbound,
const std::string &ubound) {
dType val;
std::istringstream isa(lbound);
while(isa >> val) {
bpair.first = val;
}
std::istringstream isb(ubound);
while(isb >> val) {
bpair.second = val;
}
}
// A method to set boundary map
void Boundaries::setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb) {
for(auto s : xtb) {
char type = s.second[1][0];
switch(type) {
case 'd': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<double>(
s.second[2], s.second[3]);
bounds.insert(opair);
}
break;
case 'i': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<int>(
s.second[2], s.second[3]);
bounds.insert(opair);
break;
}
case 'c': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<std::string>(
s.second[2], s.second[2]);
bounds.insert(opair);
break;
}
}
}
}
This compiles ok using g++. When I try to run it though ( as follows):
int main() {
Data D;
Boundaries B;
std::ifstream iss("tphinit.txt");
D.read_lines(iss);
auto dbounds = D.get_xtypebound();
B.setboundmap(dbounds);
auto tbounds = B.getbounds();
auto sbound = tbounds["X1"];
std::cout << sbound->bpair.first << ","
<< sbound->bpair.second << std::endl;
}
I get 'class Boundbase' has no member named 'bpair' which is true because I am pointing to the base class and not the derived class. As far as I can tell, trying to get the derived member bpair requires that I use the visitor pattern. Now, it is clear that I am noob so when I had a look at different ways of doing this on SO I was a little in over my head (no reflection on the authors, just on my inexperience).
So my main question is: Is this the best and simplest way to go about this? I would like to avoid boost::variant if at all possible (mainly for the sake of purity: this cannot be that difficult). A sub-question is whether I have to use the visitor pattern or is there a better/simpler way to get the member pbair?
I will have to perform this lookup many times so I am hoping to make it as fast as possible but using the stl for the sake of simplicity.
Make your values std variants over the 3 types.
Failing that, boost variant.
Std and boost variant really are what you want. You'll end up implementing some subset of its implementation.
Failing that, find a tutorial on how to implement ones of them, or use std any. Failing that, dynamic casts around an otherwise useless wrapper type with a virtual dtor stored in a unique ptr, or do manual RTTI with try get methods.
This just gets increasingly ugly and/or inefficient however.
Boost variant, and std variant from it, was implemented for a reason, and that reason was solving the exact problem you are describing in an efficient manner.
#include <tuple>
#include <utility>
#include <string>
template<class...Ts>
struct destroy_helper {
std::tuple<Ts*...> data;
destroy_helper( std::tuple<Ts*...> d ):data(d){}
template<class T>
static void destroy(T* t){ t->~T(); }
template<std::size_t I>
void operator()(std::integral_constant<std::size_t, I>)const {
destroy( std::get<I>( data ) );
}
};
struct construct_helper {
template<class T, class...Args>
void operator()(T* target, Args&&...args)const {
::new( (void*)target ) T(std::forward<Args>(args)...);
}
};
template<std::size_t...Is>
struct indexes {};
template<std::size_t N, std::size_t...Is>
struct make_indexes:make_indexes<N-1, N-1, Is...> {};
template<std::size_t...Is>
struct make_indexes<0, Is...>{
using type=indexes<Is...>;
};
template<std::size_t N>
using make_indexes_t = typename make_indexes<N>::type;
template<class F>
void magic_switch( std::size_t i, indexes<>, F&& f ) {}
template<std::size_t I0, std::size_t...Is, class F>
void magic_switch( std::size_t i, indexes<I0,Is...>, F&& f )
{
if (i==I0) {
f( std::integral_constant<std::size_t, I0>{} );
return;
}
magic_switch( i, indexes<Is...>{}, std::forward<F>(f) );
}
template<class T0>
constexpr T0 max_of( T0 t0 ) {
return t0;
}
template<class T0, class T1, class...Ts>
constexpr T0 max_of( T0 t0, T1 t1, Ts... ts ) {
return (t1 > t0)?max_of(t1, ts...):max_of(t0, ts...);
}
template<class...Ts>
struct Variant{
using Data=typename std::aligned_storage< max_of(sizeof(Ts)...), max_of(alignof(Ts)...)>::type;
std::size_t m_index=-1;
Data m_data;
template<std::size_t I>
using alternative_t=typename std::tuple_element<I, std::tuple<Ts...>>::type;
using pointers=std::tuple<Ts*...>;
using cpointers=std::tuple<Ts const*...>;
template<class T> T& get(){ return *reinterpret_cast<T*>(&m_data); }
template<class T> T const& get() const { return *reinterpret_cast<T*>(&m_data); }
template<std::size_t I>
alternative_t<I>& get(){ return std::get<I>(get_pointers()); }
template<std::size_t I>
alternative_t<I> const& get()const{ return std::get<I>(get_pointers()); }
pointers get_pointers(){
return pointers( (Ts*)&m_data... );
}
cpointers get_pointers()const{
return cpointers( (Ts const*)&m_data... );
}
std::size_t alternative()const{return m_index;}
void destroy() {
if (m_index == -1)
return;
magic_switch(m_index, make_indexes_t<sizeof...(Ts)>{}, destroy_helper<Ts...>(get_pointers()));
}
template<std::size_t I, class...Args>
void emplace(Args&&...args) {
destroy();
construct_helper{}( std::get<I>(get_pointers()), std::forward<Args>(args)... );
m_index = I;
}
Variant()=default;
Variant(Variant const&)=delete;//todo
Variant&operator=(Variant const&)=delete;//todo
Variant(Variant &&)=delete;//todo
Variant&operator=(Variant &&)=delete;//todo
~Variant(){destroy();}
};
int main() {
Variant<int, double, std::string> bob;
bob.emplace<0>( 7 );
bob.emplace<1>( 3.14 );
bob.emplace<2>( "hello world" );
}
here is a really simple variant interface.
The hard part is turning a runtime index into which of the compile time indexes you want to use. I call that the magic switch problem.
You might also want to implement apply visitor.
...
Or...
template<class T>
struct Derived;
struct Base {
virtual ~Base() {}
template<class T>
friend T* get(Base* base) {
Derived<T>* self = dynamic_cast<T*>(base);
return self?&self.t:nullptr;
}
template<class T>
friend T const* get(Base const* base) {
Derived<T> const* self = dynamic_cast<T const*>(base);
return self?&self.t:nullptr;
}
};
template<class T>
struct Derived:Base {
Derived(T in):t(std::move(in)){}
T t;
};
std::unordered_map<std::string, std::unique_ptr<Base>> map;
map["hello"] = std::unique_ptr<Base>( new Derived<int>(-1) );
map["world"] = std::unique_ptr<Base>( new Derived<double>(3.14) );
int* phello = get<int>(map["hello"]);
if (phello) std::cout << *hello << "\n";
double* pworld = get<double>(map["world"]);
if (pworld) std::cout << *world << "\n";
which is a seriously bargain-basement std::any.
I want to store a pair of member get/set functions for later use with an object of type T.
I have a partially working setup, see below. Questions remain however:
How do I (smartly) deal with all possible variants? member_get could be [returning value or const value& or even value& | const or non-const] member_set could be [accepting const &, & or &&]. Sure, 'best practices' would rule out some combinations, but I cannot rely on that as the definition of member_get and member_set is out of my hands.
How do I correctly deal with possible member_set move semantics?
Is there a different/better/simpler general way to approach this?
Notes:
I intentionally left open the exact type S of the setter. Not sure if that's a good or bad idea.
Lambdas obviously come to mind, but I can't see how they can help the issue. The caller of Make( get, set ) is not supposed to supply lambdas. That would be just delegating the problem to him!?
any std::function ideas should be ruled out because of the overhead
template <typename T, typename V, typename G, typename S>
class GetSet
{
public:
constexpr GetSet( G member_get, S member_set ) : Get( member_get ), Set( member_set )
{ }
auto GetValue( const T& t ) const
{
return ( t.*Get )( );
}
void SetValue( T& t, V&& value ) const
{
( t.*Set )( std::forward<V>( value ) );
}
private:
G Get;
S Set;
};
template <typename T, typename ValueType, typename S>
constexpr auto Make( ValueType( T::*member_get )( ) const, S member_set )
{
using G = ValueType( T::* )( ) const;
return GetSet<T, ValueType, G, S>( member_get, member_set );
}
Not sure why you need this, but the simplest solution is below.
#include <utility>
template <class F> struct ClassType;
template <typename Ret, typename TCls, typename... Args>
struct ClassType<Ret (TCls::*)(Args...)> {
using type = TCls;
};
template <typename Ret, typename TCls>
struct ClassType<Ret (TCls::*)() const> {
using type = TCls;
};
template<class TFnGet, class TFnSet>
class GetSet
{
public:
using TGet = TFnGet;
using TSet = TFnSet;
public:
inline GetSet(TGet fnGet, TSet fnSet)
: m_fnGet(fnGet), m_fnSet(fnSet)
{
}
template<class T>
inline decltype(auto) GetValue(T&& r) const
{
static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnGet>::type>::value, "wrong type of r?");
return (r.*m_fnGet)();
}
template<class T, class TValue>
inline void SetValue(T&& r, TValue&& value)
{
static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnSet>::type>::value, "wrong type of r?");
(r.*m_fnSet)(std::forward<TValue>(value));
}
private:
TGet m_fnGet;
TSet m_fnSet;
};
template<class TGet, class TSet>
GetSet<TGet, TSet> MakeGetSet(TGet fnGet, TSet fnSet)
{
static_assert(std::is_same<typename ClassType<TGet>::type, typename ClassType<TSet>::type>::value, "members of different classes?");
return GetSet<TGet, TSet>(fnGet, fnSet);
}
verified with:
class A
{
public:
void Set(int i) {}
int Get() const { return 0;}
void SetRef(char& ch) {}
A& GetRef() { return *this;}
void SetRRef(float&& ) {}
int&& GetRRef() { return 1; }
void SetConstRef(const char& ch) {}
int GetNotConst() { return 0;}
};
int main(int argc, char* argv[])
{
A a;
auto gs = MakeGetSet(&A::Get, &A::Set);
auto gsRef = MakeGetSet(&A::GetRef, &A::SetRef);
auto gs2 = MakeGetSet(&A::GetRRef, &A::SetRRef);
auto gsNonConst = MakeGetSet(&A::GetNotConst, &A::SetConstRef);
int x = gs.GetValue(a);
gs.SetValue(a, 2);
const A& ra = a;
x = gs.GetValue(ra);
A& r = gsRef.GetValue(a);
char ch =' ';
gsRef.SetValue(a, ch);
x = gs2.GetValue(a);
gs2.SetValue(a, 1.1f);
x = gsNonConst.GetValue(a);
gsNonConst.SetValue(a, ch);
std::cout << "ok\n";
return 0;
}
Given this sample code, how to use pars to call a constructor to create a Foo2D object?
#include <boost/hana/tuple.hpp>
#include <boost/hana/unpack.hpp>
class Foo2D {
public:
Foo2D(int x, int y):m_x(x), m_y(y) {}
private:
int m_x;
int m_y;
};
int main() {
auto pars = boost::hana::make_tuple(10, 20);
Foo2D foo1(10, 20); //my intention
Foo2D foo2 = boost::hana::unpack(pars, Foo2D); //fails
}
Constructors are not function or functor. You might use lambda or regular functions:
boost::hana::unpack(pars,
[](auto&&... args) {
return Foo2D(std::forward<decltype(args)>(args)...);
});
template<class T>
constexpr auto ctor = [](auto&&...args)
noexcept(noexcept(T{ decltype(args)(args)... }))
-> decltype( T{ decltype(args)(args)... } )
{
return T{ decltype(args)(args)... };
};
Now:
Foo2D foo2 = boost::hana::unpack(pars, ctor<Foo2D>);
should work.
Or:
// Object that represents constructing an object of type T:
template<class T>
struct ctor_t {
template<class...Args>
constexpr auto operator()(Args&&...args) const
// propogate noexcept:
noexcept(noexcept(T{ std::forward<Args>(args)... }))
// SFINAE friendly:
-> decltype( T{ std::forward<Args>(args)... } )
{
// notice this expression is repeated twice above:
return T{ std::forward<Args>(args)... };
}
template<class A0, class...Args>
constexpr auto operator()(std::initializer_list<A0> il, Args&&...args) const
noexcept(noexcept( T{ il, std::forward<Args>(args)... } ))
-> decltype(T{ il, std::forward<Args>(args)... })
{
return T{ il, std::forward<Args>(args)... };
}
constexpr ctor_t() {} // some compilers require this
template<class...Args>
constexpr (*operator T() const)(Args...) const {
return +[](Args...args)->T {
return ctor_t<T>{}(std::forward<Args>(args)...);
};
}
explicit operator bool() const = delete;
};
template<class T>
constexpr ctor_t<T> ctor{};
if you are afraid of the lambda version. This version also supports a leading initializer list and implicit cast-to-factory-function-pointer for a fixed signature.
Live example. Test code:
// construct an int 7:
auto x = ctor<int>( 7 );
std::cout << x << "\n";
// get a pointer to a constructor function
// that constructs an int from a short:
int(* pf)(short) = ctor<int>;
// run it:
std::cout << pf(0) << "\n";
My question here is similar to this post expect that I have more than one template argument and string. Thus, the setup is
class base_class; // no template args
template<typename T, typename U, typename V>
class child_class : public base_class;
I have a limited number of implemented types for T, U and V which I want to select at runtime given three strings. So as the question in cited post, I could do something like
std::unique_ptr<base_class> choose_arg1(
std::string T_str, std::string U_str, std::string v_str){
if(T_str == "int"){
return(choose_arg2<int>(U_str, V_str));
} else if(T_str == "char"){
return(choose_arg2<char>(U_str, V_str));
} // ...
}
template<typename T>
std::unique_ptr<base_class> choose_arg2(std::string U_str, std::string v_str){
if(U_str == "int"){
return(choose_arg3<T, int>(V_str));
} else if(U_str == "char"){
return(choose_arg3<T, char>(V_str));
} // ...
}
template<typename T, typename U>
std::unique_ptr<base_class> choose_arg3(std::string v_str){
if(v_str == "int"){
return(std::make_unique<child_class<T, U, int>>());
} else if(v_str == "char"){
return(std::make_unique<child_class<T, U, char>>());
} // ...
}
but is there a better way? I have less than 5^3 combination for the record.
I suggest to develop a template helper struct with a couple of static func() methods
template <typename ... Ts>
struct choose_args_h
{
using retT = std::unique_ptr<base_class>;
template <typename ... Args>
static retT func (std::string const & s, Args const & ... args)
{
if ( s == "int" )
return choose_args_h<Ts..., int>::func(args...);
else if ( s == "char" )
return choose_args_h<Ts..., char>::func(args...);
// else ...
}
static retT func ()
{ return std::make_unique<child_class<Ts...>>(); }
};
so you can write a choose_args() func simply as follows
template <typename ... Args>
std::unique_ptr<base_class> choose_args (Args const & ... args)
{ return choose_args_h<>::func(args...); }
The following is a full working example
#include <string>
#include <memory>
class base_class
{ };
template <typename, typename, typename>
class child_class : public base_class
{ };
template <typename ... Ts>
struct choose_args_h
{
using retT = std::unique_ptr<base_class>;
template <typename ... Args>
static retT func (std::string const & s, Args const & ... args)
{
if ( s == "int" )
return choose_args_h<Ts..., int>::func(args...);
else if ( s == "char" )
return choose_args_h<Ts..., char>::func(args...);
// else ...
}
static retT func ()
{ return std::make_unique<child_class<Ts...>>(); }
};
template <typename ... Args>
std::unique_ptr<base_class> choose_args (Args const & ... args)
{ return choose_args_h<>::func(args...); }
int main ()
{
auto p0 = choose_args("int", "char", "int");
auto p1 = choose_args("int", "char", "char");
}
Shown in this post is a C++17 solution with compile-time configuration of the allowed types and corresponding keys via the type Argmaps. The lookup is done by a compile-time loop.
C++11 does not support generic lambdas which are required for the compile-time loops used here. Instead, one could perform the lookup by template meta-programming with the "indices trick" (as in this online demo), but that feels too complicated and I prefer the std::map approach anyway. Note that my linked C++11 attempt could call the constructor twice if the keys are not unique.
#include <iostream>
#include <memory>
#include <string>
#include "loop.hpp"
template<class... Ts> struct Types {
static constexpr size_t size = sizeof...(Ts);
template<size_t i>
using At = std::tuple_element_t<i, std::tuple<Ts...>>;
};
template<class... Ts> constexpr Types<Ts...> to_types(Ts...) { return {}; }
template<auto... cs> struct Str {
operator std::string() const {
constexpr auto list = std::initializer_list<char>{cs...};
return std::string{list.begin(), list.end()};
}
};
template<class Char, Char... cs>
constexpr auto operator""_c() {
return Str<cs...>{};
}
//////////////////////////////////////////////////////////////////////////////
struct Base {
virtual void identify() const = 0;
};
template<class... Ts>
struct Derived : Base {
virtual void identify() const override {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
using Ptr = std::unique_ptr<Base>;
//////////////////////////////////////////////////////////////////////////////
template<class Argmaps, class Args=Types<>>
struct choose_impl;
template<class Map0, class... Maps, class... Args>
struct choose_impl<Types<Map0, Maps...>, Types<Args...>> {
static constexpr size_t pos = sizeof...(Args);
template<class S0, class... Ss>
static Ptr get(S0 s0, Ss... ss) {
Ptr ret{nullptr};
using namespace Loop;
loop(less<Map0::size>, [&] (auto i) {
using Argmapping = typename Map0::template At<i>;
using Key = typename Argmapping::template At<0>;
using Arg = typename Argmapping::template At<1>;
using Recursion = choose_impl<Types<Maps...>, Types<Args..., Arg>>;
if(std::string(Key{}) == s0) ret = Recursion::get(ss...);
});
if(!ret) {
std::cerr << "NOT MAPPED AT POS " << pos << ": " << s0 << std::endl;
std::terminate();
}
return ret;
}
};
template<class... Args>// all Args are resolved
struct choose_impl<Types<>, Types<Args...>> {
static Ptr get() {
return std::make_unique<Derived<Args...>>();
}
};
template<class Argmaps, class... Ss>
Ptr choose(Ss... ss) {
static_assert(Argmaps::size == sizeof...(Ss));
return choose_impl<Argmaps>::get(std::string(ss)...);
}
template<class V, class K>
auto make_argmapping(K) {
return Types<K, V>{};
}
//////////////////////////////////////////////////////////////////////////////
int main() {
using Argmaps = decltype(
to_types(
to_types(// first template parameter
make_argmapping<int>("int"_c),
make_argmapping<char>("char"_c),
make_argmapping<bool>("bool"_c)
),
to_types(// ... second ...
make_argmapping<double>("double"_c),
make_argmapping<long>("long"_c)
),
to_types(// ... third
make_argmapping<bool>("bool"_c)
)
)
);
choose<Argmaps>("int", "double", "bool")->identify();
choose<Argmaps>("int", "long", "bool")->identify();
choose<Argmaps>("char", "double", "bool")->identify();
choose<Argmaps>("char", "long", "bool")->identify();
choose<Argmaps>("bool", "double", "bool")->identify();
choose<Argmaps>("bool", "long", "bool")->identify();
// bad choice:
choose<Argmaps>("int", "int", "bool")->identify();
return 0;
}
loop.hpp from this unread answer:
#ifndef LOOP_HPP
#define LOOP_HPP
namespace Loop {
template<auto v> using Val = std::integral_constant<decltype(v), v>;
template<auto i> struct From : Val<i> {};
template<auto i> static constexpr From<i> from{};
template<auto i> struct Less : Val<i> {};
template<auto i> static constexpr Less<i> less{};
// `to<i>` implies `less<i+1>`
template<auto i> struct To : Less<i+decltype(i)(1)> {};
template<auto i> static constexpr To<i> to{};
template<auto i> struct By : Val<i> {};
template<auto i> static constexpr By<i> by{};
template<auto i, auto N, auto delta, class F>
constexpr void loop(From<i>, Less<N>, By<delta>, F f) noexcept {
if constexpr(i<N) {
f(Val<i>{});
loop(from<i+delta>, less<N>, by<delta>, f);
}
}
// overload with two arguments (defaulting `by<1>`)
template<auto i, auto N, class F>
constexpr void loop(From<i>, Less<N>, F f) noexcept {
loop(from<i>, less<N>, by<decltype(i)(1)>, f);
}
// overload with two arguments (defaulting `from<0>`)
template<auto N, auto delta, class F>
constexpr void loop(Less<N>, By<delta>, F f) noexcept {
loop(from<decltype(N)(0)>, less<N>, by<delta>, f);
}
// overload with one argument (defaulting `from<0>`, `by<1>`)
template<auto N, class F>
constexpr void loop(Less<N>, F f) noexcept {
using Ind = decltype(N);
loop(from<Ind(0)>, less<N>, by<Ind(1)>, f);
}
} // namespace Loop
#endif
http://coliru.stacked-crooked.com/a/5ce61617497c3bbe
As I noted in my comment, you could use a static map of string to function.
For your example code (slightly simplified to 2 template parameters to make it a little shorter), this would become:
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <memory>
class base_class { }; // no template args
template<typename T, typename U>
class child_class : public base_class { };
using ptr_type = std::unique_ptr<base_class>;
// Declarations
std::unique_ptr<base_class> choose_arg1 (std::string const & T_str,
std::string const & U_str);
template<typename T>
std::unique_ptr<base_class> choose_arg2 (std::string const & U_str);
// Definitions
std::unique_ptr<base_class> choose_arg1 (std::string const & T_str,
std::string const & U_str) {
using function_type = std::function<ptr_type(std::string const &)>;
using map_type = std::map<std::string, function_type>;
static const map_type ptrMap = {
{"int", choose_arg2<int> },
{"char", choose_arg2<char> }
};
auto ptrIter = ptrMap.find(T_str);
return (ptrIter != ptrMap.end()) ? ptrIter->second(U_str) : nullptr;
}
template<typename T>
std::unique_ptr<base_class> choose_arg2 (std::string const & U_str) {
using function_type = std::function<ptr_type()>;
using map_type = std::map<std::string, function_type>;
static const map_type ptrMap = {
{"int", []{ return std::make_unique<child_class<T, int>>(); } },
{"char", []{ return std::make_unique<child_class<T, char>>(); } }
};
auto ptrIter = ptrMap.find(U_str);
return (ptrIter != ptrMap.end()) ? ptrIter->second() : nullptr;
}
int main () {
std::cout << typeid(choose_arg1("int", "char")).name() << "\n";
std::cout << "[Done]\n";
}
If I have a struct like:
struct Thing
{
int x;
int y;
bool a;
bool b;
}
Then I can create a Thing object by doing: Thing t {1,2,true,false};. However, if I have a tuple then I am doing something like:
std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);
Thing t { std::get<0>(info), std::get<1>(info).. // and so on
Is there a better way to do this?
We can create a generic factory function for creating aggregates from tuple-like types (std::tuple, std::pair, std::array, and arbitrary user-defined tuple-like objects in a structured bindings world†):
template <class T, class Tuple, size_t... Is>
T construct_from_tuple(Tuple&& tuple, std::index_sequence<Is...> ) {
return T{std::get<Is>(std::forward<Tuple>(tuple))...};
}
template <class T, class Tuple>
T construct_from_tuple(Tuple&& tuple) {
return construct_from_tuple<T>(std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}
);
}
which in your case would be used as:
std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);
Thing t = construct_from_tuple<Thing>(info); // or std::move(info)
This way, Thing can still be an aggregate (don't have to add constructor/assignments), and our solution solves the problem for many, many types.
As an improvement, we could add SFINAE to both overloads to ensure that they're not callable with invalid tuple types.
†Pending accepting wording of how decomposition will work, the qualified call to std::get<Is> may need to be changed to an unqualified call to get<Is> which has special lookup rules. For now, this is moot, since it's 2016 and we don't have structured bindings.
Update: In C++17, there is std::make_from_tuple().
If you are using c++14 you could make use of std::index_sequence creating helper function and struct as follows:
#include <tuple>
#include <utility>
struct Thing
{
int x;
int y;
bool a;
bool b;
};
template <class Thi, class Tup, class I = std::make_index_sequence<std::tuple_size<Tup>::value>>
struct Creator;
template <class Thi, class Tup, size_t... Is>
struct Creator<Thi, Tup, std::index_sequence<Is...> > {
static Thi create(const Tup &t) {
return {std::get<Is>(t)...};
}
};
template <class Thi, class Tup>
Thi create(const Tup &t) {
return Creator<Thi, Tup>::create(t);
}
int main() {
Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}
And the version without additional class (with one additional function):
#include <tuple>
#include <utility>
struct Thing
{
int x;
int y;
bool a;
bool b;
};
template <class Thi, class Tup, size_t... Is>
Thi create_impl(const Tup &t, std::index_sequence<Is...>) {
return {std::get<Is>(t)...};
}
template <class Thi, class Tup>
Thi create(const Tup &t) {
return create_impl<Thi, Tup>(t, std::make_index_sequence<std::tuple_size<Tup>::value>{});
}
int main() {
Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}
Yet another this time tricky version with just one helper function:
#include <tuple>
#include <utility>
struct Thing
{
int x;
int y;
bool a;
bool b;
};
template <class R, class T, size_t... Is>
R create(const T &t, std::index_sequence<Is...> = {}) {
if (std::tuple_size<T>::value == sizeof...(Is)) {
return {std::get<Is>(t)...};
}
return create<R>(t, std::make_index_sequence<std::tuple_size<T>::value>{});
}
int main() {
Thing thi = create<Thing>(std::make_tuple(1,2,true,false));
}
You may use std::tie:
Thing t;
std::tie(t.x, t.y, t.a, t.b) = info;
Here are other ways:
struct Thing
{
Thing(){}
Thing(int A_, int B_, int C_, int D_) //1
: A(A_), B(B_), C(C_), D(D_) {}
Thing(std::tuple<int,int,bool,bool> tuple) //3
: A(std::get<0>(tuple)), B(std::get<1>(tuple)),
C(std::get<2>(tuple)), D(std::get<3>(tuple)) {}
void tie_from_tuple(std::tuple<int,int,bool,bool> tuple) //4
{
std::tie(A,B,C,D) = tuple;
}
int A;
int B;
bool C;
bool D;
};
inline Thing tuple_to_thing(const std::tuple<int,int,bool,bool>& tuple) //2
{
return Thing{std::get<0>(tuple), std::get<1>(tuple),
std::get<2>(tuple), std::get<3>(tuple)};
}
int main()
{
auto info = std::make_tuple(1,2,true,false);
//1 make a constructor
Thing one(info);
//2 make a conversion function
Thing second = tuple_to_thing(info);
//3 don't use tuple (just use the struct itself if you have to pass it)
Thing three{1,2,true,false};
//4 make member function that uses std::tie
Thing four;
four.tie_from_tuple(info);
}
Provide an explicit constructor and assignment operator:
struct Thing
{
int x;
int y;
bool a;
bool b;
Thing() { }
Thing( int x, int y, bool a, bool b ): x(x), y(y), a(a), b(b) { }
Thing( const std::tuple <int, int, bool, bool> & t )
{
std::tie( x, y, a, b ) = t;
}
Thing& operator = ( const std::tuple <int, int, bool, bool> & t )
{
std::tie( x, y, a, b ) = t;
return *this;
}
};
Hope this helps.
Getting fancy with C++17 template argument deduction and using a proxy object (usage example at bottom):
#include <tuple>
using namespace std;
template <class Tuple>
class FromTuple {
// static constructor, used to unpack argument_pack
template <class Result, class From, size_t... indices>
static constexpr Result construct(index_sequence<indices...>, From&& from_tuple) {
return { get<indices>(forward<
decltype(from_tuple.arguments)>(from_tuple.arguments))... };
}
// used to select static constructor
using Indices = make_index_sequence<
tuple_size_v< remove_reference_t<Tuple> >>;
public:
// construct with actual tuple types only for parameter deduction
explicit constexpr FromTuple(const Tuple& arguments) : arguments(arguments) {}
explicit constexpr FromTuple(Tuple&& arguments) : arguments(move(arguments)) {}
// implicit cast operator delegates to static constructor
template <class Result>
constexpr operator Result() { return construct<Result>(Indices{}, *this); }
private:
Tuple arguments;
};
struct Thing { int x; int y; bool a; bool b; };
int main() {
std::tuple<int, int, bool, bool> info = std::make_tuple(1,2,true,false);
Thing thing0((Thing)FromTuple(info));
Thing thing1{(Thing)FromTuple(info)};
FromTuple from_info(info);
Thing thing2(from_info); // only way to avoid implicit cast operator
Thing thing3{(Thing)from_info};
return 0;
}
This generalizes to any class or struct, not just Thing. Tuple arguments will be passed into the constructor.