I'd like to have a class that gets in it's Ctor unlimited parameters of the same type, and stores them into a vector. It should look like that:
class A(int a, int b, **N time parameter of type T**)
: data(**vector will get N times type T**)
{
}
protected:
vector<T> data;
How should I implement it? Solution could be in c++11/14
I got a few errors such as "parameter packs not expanded with ‘…' ", etc..
This code sample might be useful:
#include <vector>
#include <utility>
template<typename T>
class MyClass {
public:
template<typename ...Args>
MyClass(int a, int b, Args&& ...args) :data{ std::forward<Args>(args)... } {}
private:
std::vector<T> data;
};
int main() {
MyClass<char> sample(1, 2, 'a', 'b');
return 0;
}
[EDIT]: Added std::forward, added missing include for utility
Assuming that T could be anything, even something quite large or non-copyable, we'd want to:
preserve efficiency with perfect forwarding.
check types.
std::initializer_list satisfies 2 but not 1.
Simple variadic template expansion satisfies 1 and not 2.
This solution uses variadic template expansion and enable_if to enforce type compatibility.
#include <vector>
#include <utility>
#include <string>
namespace detail
{
constexpr bool all()
{
return true;
}
template<class...Rest>
constexpr bool all(bool b, Rest...rest)
{
return b and all(rest...);
};
}
template<class T>
class A
{
public:
using value_type = T; // say
template<class...Rest,
std::enable_if_t<detail::all(std::is_convertible<Rest, value_type>::value...)>* = nullptr>
A(int a, int b, Rest&&...rest)
: a_(a), b_(b)
{
this->fill(std::forward_as_tuple(std::forward<Rest>(rest)...),
std::make_index_sequence<sizeof...(Rest)>());
}
private:
template<class Tuple, std::size_t...Is>
void fill(Tuple&& t, std::index_sequence<Is...> seq)
{
data_.reserve(seq.size());
using expand = int[];
void(expand{ 0,
(data_.push_back(std::move(std::get<Is>(t))), 0)...
});
}
private:
int a_, b_;
std::vector<value_type> data_;
};
int main()
{
using namespace std::literals;
auto a = A<double>(1, 2, 4.3, 5.5, 6.6);
auto b = A<std::string>(1, 2, "the", "cat"s, "sat on the mat");
// error: no matching constructor...
// auto err = A<std::string>(1, 2, "the", "cat"s, 0.1);
}
Here you go:
#include <iostream>
#include <vector>
template<class T>
struct V
{
V(int n, std::initializer_list<T> l)
: data(l)
{
(void) n;
}
std::vector<T> data;
};
int main()
{
V<int> v(0,{1,2,3});
}
This is not a perfect example since one needs to construct an object with the weird syntax (n, {optional, arguments, of, same, type}) but it does provide wanted behavior.
The following example is similar to fr3nzy90's, but with the coming C++17 it will allow automatic deduction of T from the constructor arguments:
template <class T>
class MyContainer {
private:
std::vector<T> data;
public:
// Take the first T value explicitly so it can be used to deduce
// T from the constructor arguments (C++17 feature).
template <class... Ts>
MyContainer(int a, int b, T const & tval, Ts const &... tvals) :
data{tval, tvals...} {
…
}
// Special case, empty list, no implicit type deduction, because
// there is no T value to deduce it from.
MyContainer(int a, int b) {
…
}
};
Related
I have a vector of pointers to Base.
Invariant: only one of each derived type should be in that vector at any time.
I also want to be able to lookup the value with a given type in O(1). I can do this in O(n) easily, by checking dynamic_cast.
Basically, I want to replace my vector with a map or something. Is that possible?
Here's minimal example with the vector and the loop:
#include <functional>
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
using namespace std;
typedef struct Base {
virtual ~Base(){};
} Base;
vector<unique_ptr<Base>> baseList;
template <typename NarrowType,
typename std::enable_if_t<
! std::is_same_v<Base, NarrowType> &&
std::is_base_of_v<Base, NarrowType>,
bool> = true>
void ApplyFuncToType(function<void(NarrowType)> func) {
// Want to get rid of this loop
for (auto &base : baseList) {
NarrowType *narrow = dynamic_cast<NarrowType *>(base.get());
if (narrow) {
func(*narrow);
}
}
}
// usage
int main() {
typedef struct A : Base {
void printA() { cout << "a" << endl; }
} A;
typedef struct B : Base {
void printB() { cout << "b" << endl; }
} B;
baseList.push_back(make_unique<A>());
baseList.push_back(make_unique<B>());
ApplyFuncToType<A>([](A a) { a.printA(); });
}
Questions:
How can I enfore my invariant (one of each type max in container)
Would a unordered_map<type_info, unique_ptr<Base>> be a good solution to this? I have read that typeid is not consistent or safe to use or something, but am not sure exactly.
Edits/Clarification:
This is for a system where other classes can register their own types in this vector. i.e. the contents of the vector will change during runtime.
A similar approach is shown here, where an unordered_map is used to allow self-registered event callbacks.
Yeah, sure, it's possible, but I'm not convinced you need it. After all, all your types are completely static.
Also, ApplyFuncToType shouldn't be taking std::function, but a generic argument, since you'll save on the cost of shoehorning things into std::function. You're not deducing any types anyway - because std::function is not a tool for that - and thus you have the call that includes the type parameter explicitly: ApplyFuncToType<A>.
And finally, it's probably wrong to pass A and B to the lambda by value - since then the instance the lambda is using is not the instance you so carefully deposited beforehand (!). It should be passed by const reference, or by reference if it's a non-const method:
// Do this
ApplyFuncToType<A>([](const A &a) { a.printA(); });
// Or do that
ApplyFuncToType<A>([](A &a) { a.printA(); });
// NO!
ApplyFuncToType<A>([](A a) { a.printA(); });
It's hard to deduce it ahead of time, but I imagine that you'd want to make A, B, ... non-copyable but they definitely should be movable (read on).
A Tuple of Pointers
All you really want is the below - and it doesn't care that the types are derived from some base, you can use any types you wish. You can of course add type constraints if you want to protect from bugs where wrong types are supplied to ptr_tuple.
#include <functional>
#include <memory>
#include <tuple>
struct A { void methodA() {} };
struct B { void methodB() {} };
template <class ...Args>
using ptr_tuple = std::tuple<std::unique_ptr<Args>...>;
ptr_tuple<A, B> instances;
template <typename T>
auto &instance()
{
return std::get<std::unique_ptr<T>>(instances);
}
template <class T, class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
auto *ptr = instance<T>().get();
if (ptr) {
std::invoke(fun, *ptr, std::forward<Args>(args)...);
}
}
int main() {
instance<A>() = std::make_unique<A>();
instance<B>() = std::make_unique<B>();
invoke<A>([](A& a){ a.methodA(); });
invoke<B>([](B& b){ b.methodB(); });
}
Argument Deduction for Invoke/Apply
It's not even necessary to supply the explicit type parameter to invoke. We can deduce it. For that, we use a traits class that's sorely missing in C++ standard library:
// from https://stackoverflow.com/a/39717241/1329652
// see also
// https://github.com/kennytm/utils/blob/master/traits.hpp
// https://stackoverflow.com/a/27885283/1329652
// boost::callable_traits
template <typename T, typename = void>
struct function_traits;
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const>
{
using args_type = std::tuple<A... >;
using arg0_class = std::decay_t<std::tuple_element_t<0, args_type>>;
};
template <typename T>
struct function_traits<T, std::void_t<decltype(&T::operator())> >
: public function_traits< decltype(&T::operator()) >
{};
And then we can deduce the needed type in invoke:
template <class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
using arg0_class = typename function_traits<std::decay_t<Fun>>::arg0_class;
auto *ptr = instance<arg0_class>().get();
if (ptr) {
std::invoke(fun, *ptr, std::forward<Args>(args)...);
}
}
int main() {
instance<A>() = std::make_unique<A>();
instance<B>() = std::make_unique<B>();
invoke([](A& a){ a.methodA(); });
invoke([](B& b){ b.methodB(); });
}
A Tuple of Optional Values
Depending on what your A and B types really are, if they can be moved, then using dynamic memory allocation is totally unnecessary, you'd much rather keep them by value, e.g. with optional:
#include <functional>
#include <memory>
#include <optional>
#include <tuple>
struct A { void methodA() {} };
struct B { void methodB() {} };
template <class ...Args>
using opt_tuple = std::tuple<std::optional<Args>...>;
opt_tuple<A, B> instances;
template <typename T> auto &instance()
{
return std::get<std::optional<T>>(instances);
}
template <class T, class Fun, class ...Args>
void invoke(Fun &&fun, Args &&...args)
{
auto &opt = instance<T>();
if (opt) {
std::invoke(fun, *opt, std::forward<Args>(args)...);
}
}
int main() {
instance<A>().emplace(); // constructs A
instance<B>().emplace(); // constructs B
invoke<A>([](A& a){ a.methodA(); });
invoke<B>([](B& b){ b.methodB(); });
}
Of course you can add the type-deduced variant of invoke just as before.
A type-id Stand In
Even though I really think that your original solution is in want of a problem - you should state what problem you're trying to solve, otherwise it smells of an XY problem - there of course is a better "type id" than type_id: an address of a function templated on a type. There'll be only one instance of it per program.
I don't think that the "O(1)" lookup is a real requirement, a very, very fast O(log(N)) lookup - way faster than you'd get from e.g. std::map, will work just as well for whatever your imaginary applications is.
Thus:
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <vector>
// here goes function_traits implementation from above
struct Base {};
template <typename T>
constexpr static bool is_derived_from_Base_v =
!std::is_same_v<Base, T> && std::is_base_of_v<Base, T>;
class UniqueTypeObjects {
using marker_type = void(*)();
struct Pair {
std::unique_ptr<Base> base;
marker_type marker;
Pair(std::unique_ptr<Base> &&base, marker_type marker) : base(std::move(base)), marker(marker) {}
bool operator<(marker_type o) const { return marker < o; }
};
friend bool operator<(marker_type a, const Pair &o);
template <typename T, typename = std::enable_if<is_derived_from_Base_v<T>>>
struct Witness {
static void marker() {}
};
std::vector<Pair> m_objects;
public:
template <class Derived, class =
std::enable_if_t<is_derived_from_Base_v<Derived>>>
void insert(std::unique_ptr<Derived> &&obj) {
auto constexpr marker = &Witness<Derived>::marker;
auto it = std::lower_bound(m_objects.begin(), m_objects.end(), marker);
if (it != m_objects.end() && it->marker == marker)
throw std::logic_error("Attempting to insert an object of duplicate type");
m_objects.emplace(it, std::move(obj), marker);
}
template <typename Derived, typename Fun,
class = std::enable_if_t<is_derived_from_Base_v<Derived>>>
void apply(Fun fun) const {
auto constexpr marker = &Witness<Derived>::marker;
auto it = std::lower_bound(m_objects.begin(), m_objects.end(), marker);
if (it == m_objects.end() || it->marker != marker)
throw std::runtime_error("No object found to apply the function to");
std::invoke(fun, *static_cast<Derived*>(it->base.get()));
}
template <typename Fun,
class = std::enable_if_t<is_derived_from_Base_v<
typename function_traits<std::decay_t<Fun>>::arg0_class>>>
void apply(Fun fun) const {
using arg0_class = typename function_traits<std::decay_t<Fun>>::arg0_class;
apply<arg0_class>(std::move(fun));
}
};
bool operator<(void(*a)(), const UniqueTypeObjects::Pair &o)
{ return a < o.marker; }
char lastInvoked;
int main() {
struct A : Base {
void methodA() { lastInvoked = 'A'; }
};
struct B : Base {
void methodB() { lastInvoked = 'B'; }
};
UniqueTypeObjects uto;
uto.insert(std::make_unique<A>());
uto.insert(std::make_unique<B>());
assert(!lastInvoked);
uto.apply([](A &a){ a.methodA(); });
assert(lastInvoked == 'A');
uto.apply([](B &b){ b.methodB(); });
assert(lastInvoked == 'B');
}
But I still don't think it's necessary. If you truly have O(1) requirement, e.g. some sort of a realtime system, or system with deterministic execution timing, then the opt_tuple solution or its equivalent is the one you should use. Otherwise - good luck with the paperwork and test plans to ensure that UniqueTypeObjects works. I wrote the thing and even I wouldn't allow it in a realtime or hi-reliability codebase I maintained. Nothing beats static type safety and ensuring correctness by design, and you get that with the tuple approach (or its equivalent with a custom class).
I'm trying to implement a reader functor in C++.
Corresponding Haskell definition is fmap :: (a -> b) -> (r -> a) -> (r -> b)
My C++ version is:
template<class A, class B, class R>
B fmap(const std::function<B(A)> &funcA, const std::function<A(R)> &funcR) {
return funcA(funcR());
}
std::string function_1(int n);
double function_2(std::string s);
fmap(function_2, function_1);
The error is:
note: candidate template ignored: could not match 'function<type-parameter-0-1 (type-parameter-0-0)>' against 'double (std::__1::basic_string<char>)'
B fmap(const std::function<B(A)> &funcA, const std::function<A(R)> &funcR) {
What is the correct way to implement fmap function?
You can do this with a neat template conversion trick from Template type deduction with std::function
#include <functional>
#include <iostream>
#include <string>
using namespace std;
template<class T>
struct AsFunction
: public AsFunction<decltype(&T::operator())>
{};
template<class ReturnType, class... Args>
struct AsFunction<ReturnType(Args...)> {
using type = std::function<ReturnType(Args...)>;
};
template<class ReturnType, class... Args>
struct AsFunction<ReturnType(*)(Args...)> {
using type = std::function<ReturnType(Args...)>;
};
template<class Class, class ReturnType, class... Args>
struct AsFunction<ReturnType(Class::*)(Args...) const> {
using type = std::function<ReturnType(Args...)>;
};
template<class F>
auto toFunction(F f) -> typename AsFunction<F>::type {
return { f };
}
template<class A, class B, class R>
B fmap(const std::function<B(A)>& funcA, const std::function<A(R)>& funcR, R value) {
return funcA(funcR(value));
}
template <class T>
auto ToFunction(T t) {
return t;
}
std::string function_1(int n) {
return ""s;
}
double function_2(std::string s) {
return 0.0;
}
int main() {
fmap(toFunction(function_2), toFunction(function_1), 5);
return 0;
}
The issue is that template deduction works with exact match on type, without conversions.
You are passing in function pointers, which is not the same type as std::function, so deduction of the template parameters will fail.
The correct way is to take in the callables as template arguments. This ensures deduction will work. A lot of times you don't need to check the signature of the callable, since if it's used in the function you will get a compile time error if it's used in the wrong way.
If you still want to check the signature it's not very hard to do with a type trait.
#include <string>
template<class A, class B>
B fmap(A a, B b) {
return a(b(std::string{}));
}
std::string function_1(int n);
double function_2(std::string s);
fmap(function_2, function_1);
Bartosz Milewski's book "Category Theory for Programmers" (2014-19)
https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
gives an example of the Writer functor in C++ ... and it's a simpler step from there to produce the Reader functor:
#include <string>
#include <functional>
using namespace std;
template<class R, class A, class B>
function<B(R)> Reader(function<A(R)> m1, function<B(A)> m2)
{
return [m1,m2] (R r) { return m2(m1(r)); };
}
// example
string repeat(string x) {return x+x;}
string i_to_s( int x) {return to_string(x);}
string process(int x) {
return Reader<int, string, string>(i_to_s, repeat)(x);}
With reference to c++11 list initialization, may I initialize a list with an element and another list?
Let's say I have the following code:
#include <vector>
class Foo
{
public:
Foo(int value){m_v=value;}
private:
int m_v = 0;
};
int main()
{
std::vector<Foo> v1, v2, v3;
v1 = {Foo(1)}; //ok
v2 = {Foo(2), Foo(3)}; //ok
v3 = {Foo(3), v2}; //error: no match for ‘operator=’ (operand types are ‘std::vector’ and ‘’)
}
Is there a way to create in one line of code, using list initialization, a vector made of the element of another vector plus a new element (a prepend, in the example above).
We can create some template infrastructure to allow creation of vectors through optional concatenation of objects and other vectors.
This is very much a first cut:
#include <utility>
#include <vector>
namespace extended
{
template<class T>
struct appender
{
template<class V, class A, class Arg>
void operator()(std::vector<V, A>& vec, Arg&& arg) const
{
vec.push_back(std::forward<Arg>(arg));
}
};
template<class V2, class A2>
struct appender<std::vector<V2, A2>>
{
template<class V, class A, class X>
void operator()(std::vector<V, A>& vec, X&& arg) const
{
vec.insert(end(vec), begin(std::forward<X>(arg)), end(std::forward<X>(arg)));
}
};
template<class V, class A, class T>
auto append(std::vector<V, A>& target, T&& x) -> decltype(auto)
{
auto op = appender<std::decay_t<T>>();
op(target, std::forward<T>(x));
return target;
}
}
template<class T, class...Args>
auto make_vector(Args&&...args)
{
using extended::append;
std::vector<T> result;
using expand = int[];
expand {0,
(append(result, std::forward<Args>(args)), 0)...
};
return result;
}
class Foo
{
public:
Foo(int value){m_v=value;}
private:
int m_v = 0;
};
int main()
{
auto v1 = make_vector<Foo>(Foo(1)); //ok
auto v2 = make_vector<Foo>(Foo(2), Foo(3)); //ok
auto v3 = make_vector<Foo>(Foo(3), v2); //ok
}
Of course, by looking for common interfaces we can start to push the boundaries a little:
#include <utility>
#include <iterator>
#include <vector>
#include <list>
#include <set>
namespace extended
{
// The general case of an appender.
// simply calls emplace_back
template<class T, class Diff = void>
struct appender
{
template<class V, class A, class Arg>
void operator()(std::vector<V, A>& vec, Arg&& arg) const
{
vec.emplace_back(std::forward<Arg>(arg));
}
};
// specific specialisation for an appender where the
// source object supports begin() and end() (i.e. a container)
//
template<class T>
struct appender
<
T,
decltype(
std::begin(std::declval<T>()),
std::end(std::declval<T>()),
void()
)
>
{
template<class V, class A, class X>
void operator()(std::vector<V, A>& vec, X&& arg) const
{
vec.insert(std::end(vec), std::begin(std::forward<X>(arg)), std::end(std::forward<X>(arg)));
}
};
template<class V, class A, class T>
auto append(std::vector<V, A>& target, T&& x) -> decltype(auto)
{
auto op = appender<std::decay_t<T>>();
op(target, std::forward<T>(x));
return target;
}
}
template<class T, class...Args>
auto make_vector(Args&&...args)
{
using extended::append;
std::vector<T> result;
using expand = int[];
expand {0,
(append(result, std::forward<Args>(args)), 0)...
};
return result;
}
class Foo
{
public:
Foo(int value){m_v=value;}
bool operator<(const Foo& r) const { return m_v < r.m_v; }
private:
int m_v = 0;
};
int main()
{
auto v1 = make_vector<Foo>(Foo(1)); //ok
auto v2 = make_vector<Foo>(Foo(2), Foo(3)); //ok
auto v3 = make_vector<Foo>(Foo(3), v2); //ok
auto v4 = make_vector<Foo>(Foo(1),
std::list<Foo> { Foo(2), Foo(3) },
make_vector<Foo>(4, make_vector<Foo>(8, 9, 10)),
std::set<Foo> {Foo(6), Foo(7) }); // bizzare but ok
}
std::vector<Foo> means a std::vector of Foo instances. This means it cannot arbitrarily store other std::vector instances, which is what you're asking the compiler when writing
v3 = {Foo(3), v2};
std::initializer_list<T> is a homogeneous collection of T instances. std::vector<Foo>'s list constructor takes std::initializer_list<Foo>. There's no way to achieve what you want without manually unpacking v2's elements inside the curly braces.
Is there a way to create in one line of code, using list initialization, a vector made of the element of another vector plus a new element (a prepend, in the example above).
Using list initialization, no. You can write your own function to achieve the same thing, however.
In C++11, is there a DRY way to construct all elements of an array with some same set of parameters for all elements? (e.g. via a single initializer list?)
For example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
// This would construct just the first object with a parameter of 1.
// For the second and third object the default ctor will be called.
C ar[3] {1};
// This would work but isn't DRY (in case I know I want all the elements in the array to be initialized with the same value.
C ar2[3] {1, 1, 1};
// This is DRYer but obviously still has repetition.
const int initVal = 1;
C ar3[3] {initVal, initVal, initVal};
I know my goal is easily achievable by using an std::vector. I'm wondering if it's possible with raw arrays as well.
c++14 - a little work will make this work for c++11
#include <iostream>
#include <array>
#include <utility>
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
namespace detail {
template<class Type, std::size_t...Is, class...Args>
auto generate_n_with(std::index_sequence<Is...>, const Args&...args)
{
return std::array<Type, sizeof...(Is)> {
{(void(Is), Type { args... })...} // Or replace '{ args... }' with '( args... )'; see in comments below.
};
}
}
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
}
int main()
{
auto a = generate_n_with<C, 3>(1);
for (auto&& c : a)
{
std::cout << c.m_x << std::endl;
}
}
results:
1
1
1
I want to guarantee no copies prior to c++17
The you would need to generate into a vector:
template<class Container, class...Args>
auto emplace_n(Container& c, std::size_t n, Args const&...args)
{
c.reserve(n);
while(n--) {
c.emplace_back(args...);
}
};
used like this:
std::vector<C> v2;
emplace_n(v2, 3, 1);
You can construct a sequence of elements using an std::index_sequence<...> and expand that into the initializers of an array. I don't know of any approach avoiding an auxiliary function, though. Here is an example:
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
template <typename T, std::size_t... I>
std::array<T, sizeof...(I)> fill_aux(T value, std::index_sequence<I...>)
{
return std::array<T, sizeof...(I)>{ (void(I), value)... };
}
template <std::size_t N, typename T>
std::array<T, N> fill(T value) {
return fill_aux(value, std::make_index_sequence<N>());
}
int main()
{
std::array<S, 10> array = fill<10>(S(17));
std::copy(array.begin(), array.end(), std::ostream_iterator<S>(std::cout, " "));
}
By creating derived class, you can effectively create a new default value. It's a bit hackish, but may be less hackish than other solutions. Here's an example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
template <int init>
struct CInit : C { CInit() : C(init) {} };
CInit<1> ar2[3];
const int initVal = 1;
CInit<initVal> ar3[3];
Another approach is to wrap your raw array inside a struct with a variadic constructor:
template <size_t n>
struct Array {
C array[n];
template <size_t... seq>
Array(int init,std::index_sequence<seq...>)
: array{(void(seq),init)...}
{
}
Array(int init)
: Array(init,std::make_index_sequence<n>())
{
}
};
const int initVal = 1;
Array<3> ar3_1(initVal);
const C (&ar3)[3] = ar3_1.array;
Building on Richard's answer, it's also possible to define
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const std::array<Type, N>&, const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
};
Allowing you to enter the array as a parameter to make the code more dry in case you already know the type of the array, e.g.
class D {
public:
D();
std::array<int, 3> m_ar;
};
Allowing
D::D() : m_ar{generate_n_with{m_ar, 5}} {}
Instead of the less DRY
D::D() : m_ar{generate_n_with<int, 3>{5}} {}
P.S. maybe there's an even DRYer way without repeating m_ar twice?
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.