Implement getting value by index for a custom variant class - c++

My question may look obvious and even incorrect, so I'll describe my problem from the very beginning. As it often happens, I may be misunderstanding the concept itself, therefore trying to do something which should be done in a totally different way.
Subj
I've been challenged to write a simple custom version of std::variant. It's key features (when implementing one) are:
Works like a type-dafe union
Does not allocate extra memory dynamically
Is properly aligned, according to the contained types
There exist get<T> & get<index> functions (in my case), which can obatin variant's data by type & index (that's where trouble began)
Problem
To achieve all the copy & move semantics the stored type is required to be available, so that if it is not POD, it's constructors and other stuff would be called. Also seems that get<index>() relies upon the same ability to get that type. Would be fine... but it's obtained only in constructor, which means runtime.
Below is my small draft, it stores values and supports get<T>:
/* Here goes template magic, don't read much of it,
* should be working fine, just showing relevant part of it
************************************************************/
inline constexpr std::size_t variant_npos = -1;
template<typename _Tp, typename... _Types>
struct __index_of : std::integral_constant<size_t, 0> {};
template<typename _Tp, typename... _Types>
struct __index_of_v
{
static constexpr size_t value = __index_of<_Tp, _Types...>::value;
};
template<typename _Tp, typename _First, typename... _Rest>
struct __index_of<_Tp, _First, _Rest...> :
std::integral_constant<size_t, std::is_same<_Tp, _First>::value ? 0 : __index_of_v<_Tp, _Rest...>::value + 1> {};
template<typename _Tp, typename... _Types>
struct __variant_index_of // !! This can get an index of type in variant's pack. Works ok.
{
static constexpr size_t value = __index_of_v<_Tp, _Types...>::value == sizeof...(_Types) ? 0 : __index_of_v<_Tp, _Types...>::value;
};
//---------------------- Here goes the class --------------------------------
template<typename... Types>
class variant
{
const __type_index<Types...> __npos = static_cast<__type_index<Types...>>(variant_npos); // Never mind
public:
variant()
: _index(__npos) // "No type" index
{ }
variant(const variant<Types...>& other)
: _data(other._data)
{ }
variant(variant<Types...>&& other)
: _data(other._data)
{
other._index = variant_npos;
}
/* Constructors contain type check, because
* supporting implicit conversions is good, these
* checks don't influence the current question
************************************************/
template<class U, typename std::enable_if<__variant_index_of<std::decay_t<U>, Types...>::value != 0 >::type* = nullptr>
variant(U&& value)
: _index(__variant_index_of<U, Types...>::value)
{
new (getPtr()) U{std::move(value)};
}
template<class U, typename std::enable_if<__checker<0, U, Types...>::is_conv
&& __variant_index_of<std::decay_t<U>, Types...>::value == 0 >::type* = nullptr>
variant(U&& value)
: _index(__checker<0, U, Types...>::number_of_class)
{
using Datatype = typename __checker<0, U, Types...>::TargetClass;
new (getPtr()) Datatype{std::move(value)};
}
variant<Types...>& operator=(const variant<Types...>& other)
{
// TODO: should be improved, as well as move should be implemented
if (this != &other)
{
_data = other._data;
_index = other._index;
}
return *this;
}
std::size_t index() const
{ return _index; }
bool empty() const
{ return _index == __npos; }
private:
void* getPtr() const
{ return const_cast<void*>(static_cast<const void*>(std::addressof(_data))); }
void* getPtr()
{ return static_cast<void*>(std::addressof(_data)); }
private:
std::aligned_union_t<1, Types...> _data;
// Taken from GCC implementation, a sophisticated index type for alignment
// Assume it is an unsigned number
__type_index<Types...> _index;
static_assert(sizeof...(Types) > 0, "There must be at least one type alternative");
template<typename T, typename... OtherTypes>
friend T& get(variant<OtherTypes...>& v);
template<typename T, typename... OtherTypes>
friend const T& get(const variant<OtherTypes...>& v);
template<typename T, typename... OtherTypes>
friend T* get(variant<OtherTypes...>* v);
template<typename T, typename... OtherTypes>
friend const T* get(const variant<OtherTypes...>* v);
template<typename T, typename... OtherTypes>
friend T&& get(const variant<OtherTypes...>&& v);
};
//----------------- The latter 3 get functions are almost similar ------
template<typename T, typename... Types>
T& get(variant<Types...>& v)
{
return const_cast<T&>(get<T>(const_cast<const variant<Types...>&>(v)));
}
template<typename T, typename... Types>
const T& get(const variant<Types...>& v)
{
if ((v._index == variant_npos) || (v._index != __variant_index_of<T, Types...>::value))
throw bad_get("variant get error");
return *reinterpret_cast<T*>(v.getPtr());
}
The code is not "minimal", sorry for that, but if shows my effort. In short: index of type is stored in constructors, there's been also implemented a __variant_index_of thing, so that index of any given type can be searched in the variant's template parameters.
It looked like a nice idea then to implement something like:
template<size_t TypeIndex, typename... Types>
using variant_type_getter_t = typename variant_type_getter<TypeIndex, Types...>::type;
Can include it's possible implementation here, but it doesn't solve the problem: occured that _index is a runtime value, so that templates are of no use, lots of ‘this’ is not a constant expression errors at compile-time prove that.
Spending pretty lots of time reading the existing sources (like GCC's implementation) lead to a possibly wrong confidence that using classic type erasure (pImpl -> template child parameterized with the stored type) should not be applied here.
Question
So, as the title states: is it even possible to use some trick for implementing access to the type of a variadic template by it's runtime index?
Maybe some template redesign is required for that? Or even maybe the stack allocation of data had not been thought through properly?
Thanks in advance for any bright ideas =)
UPD: renamed the question to emphasize, that I basically know, what are templates, just cannot cook them =(

Related

How to implement an 'Observer' class, using std::invoke for redirecting function calls?

so I'm trying to design a class Observed<T>, which stores a T and bool, with the boolean value indicating whether the T member variable has been updated since it was last set. To demonstrate, my initial implementation was something like
template <std::copyable T>
class Observed
{
public:
Observed()
: m_data{}, m_updated{false} {};
Observed(const T &data)
: m_data{data}, m_updated{true} {};
// .. move, copy, assignment ctors
// setting the data leaves it 'updated'
template <typename U>
requires(std::same_as<T, U>)
auto write(U &&value) -> void
{
m_data = std::forward<U>(value);
m_updated = true;
}
// to test if it has since been modified (i.e. written without reading)
auto modified() -> bool
{
return m_updated;
}
// reading the value means it has no longer been updated
auto value() -> T &
{
m_updated = false;
return m_data;
}
private:
T m_data;
bool m_updated;
};
However, the problem became that for an example Observed<std::vector<int>> v, if I needed to add an element to the vector i'd call v.value().push_back(...), which would surpass the write() function, and subsequently m_updated would not be set correctly.
My slightly janky approach was to therefore introduce a call() function as follows:
template <std::copyable T>
class Observed
{
public:
// ctors
Observed()
: m_data{}, m_updated{false} {};
Observed(const T &data)
: m_data{data}, m_updated{true} {};
// the new 'call' function
template<typename Func, typename... Ts>
auto call(Func&& func, Ts... args) -> std::invoke_result_t<Func, Ts...>
{
m_updated = true;
return std::invoke(func, m_data, args...);
}
// .. rest of class
private:
T m_data;
bool m_updated;
};
so that you'd be able to write v.call(&std::vector<int>::push_back, 1).
However, doing so doesn't exactly work, and debugging it is a bit of a nightmare. I just wondered whether anyone could give it a look over and help me get the correct functionality. I hope my code above has shown enough of my intent with the class.
I'm also a little unclear on how you would form the function pointer to std::vector<T>::push_back given it takes an allocator as a second template argument.
Thanks.
What about two value() methods?
A const one, only for reading
T const & read_value() const
{
m_updated = false;
return m_data;
}
and a second one, to modify the content
T & write_value()
{
m_updated = true;
return m_data;
}
I have found a solution! (I think!)
So the first problem is that given vector<T>::push_back is overloaded, when we pass the function point to call we must cast it to the exact function pointer type we want i.e.
void (std::vector<int>::*f_ptr)(const int&) = &std::vector<int>::push_back;
and then we can implement the call() function as follows:
template<typename Ret, typename... Args>
auto call(Ret (T::*func)(Args...), Args... args)
-> std::invoke_result_t<Ret(T::*)(Args...), T, Args...>
{
m_updated = true;
return std::invoke(func, m_data, args...);
}
However, this then has the problem that if T of the original class is not a class itself, say T = int then T::* leads to a compilation error. If anyone has thoughts on how to resolve this, feel free to suggest some. SFINAE?
Edit: found a solution - template the function pointer on U
// if you want to update the value via a class method of T
template<typename U, typename Ret, typename... Args, typename... UArgs,
std::enable_if<!std::is_integral<U>::value, bool>::type = true
>
auto call(Ret (U::*func)(Args...), UArgs... args)
-> std::invoke_result_t<Ret(U::*)(Args...), U, UArgs...>
{
return std::invoke(func, m_data, args...);
}
And then an improved version using concepts:
template<typename Ret, typename... Args, typename U>
requires(std::same_as<T, U> && !std::is_integral_v<U>)
auto call(Ret (U::*func)(Args...), Args... args)
-> std::invoke_result_t<Ret(U::*)(Args...), U, Args...>
{
return std::invoke(func, m_data, args...);
}
https://godbolt.org/z/98ffE39Te

Transform each of parameter pack's values based on a boolean criteria

I am trying to solve this problem in C++ TMP where in i need to convert one parameter pack types into another, and then convert back the types and also values. The conversion back part is based on a boolean criteria that whether an arg in Args... was transformed or not in the first place.
Basically, i have a pack(Args...). First, i transform this (for each args[i], call a transform function). It works like this:
For each arg in Args..., just create same type in transformed_args... unless it is one of following, in that case do following conversions:
Type In Args...
Type In transformed_Args...
SomeClass
shared_ptr to SomeClass
std::vector of SomeClass
std::vector of shared_ptr to SomeClass
everything else remains the same for ex:
int remains int
std::string remains std::string
I achieve this by template specialization, of course
For the next part, i take transformed_args..., publish a class and a functor. I receive call back on this functor from(C++generated Python using Pybind, not important though). Relevant bits of that class look like this...
template<typename C, typename...transformed_args..., typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
//.....
void operator()(transformed_args... targs)
{
//....
(*func.wrapped_method_inside)(transform_back_magic(targs)...) // this is want i want to achieve.
//transform_back_magic(targs)... is a plaeholder for code that checks if type of args[i]... != type of targs[i]... and then calls a tranform_back specialization on it else just return args[i].val
}
}
targs are in transformed_args... format, but underlying C++ function they are aimed for expects Args...
template<typename... Args, typename... transformed_args, ........whatever else is needed>
transform_back_magic(....)
{
if(Args[i].type != transformed_args[i].types)
tranform_back(targs[i]...);
}
the tranform_back function template logic is specialized for different cases and all logic is in place. But how to invoke that based on this boolean criteria is hitting my TMP knowledge limits. I just got started not many weeks ago.
Here i am listing down what i have created so far.
First of all this is what i need in pseudo code
template<typename C, typename... transformed_args, typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
void operator(transformed_args... targs)
{
**//In pseudo code, this is what i need**
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
(*func.wrapped_method_inside)(params...);
}
}
In my attempt to implement this, so far I have decided on creating a tuple<Args...> object by copying data from targs(with conversions where ever required)
void operator(transformed_args... targs)
{
//....
auto mytup = call1(std::tuple<args...>(), std::make_index_sequence<sizeof...(Args)>,
std::make_tuple(targs...), targs...);
// mytup can be std::tuple<Args...>(transform_back(1st_targs), transform_back(2nd_targs)....). Once available i can write some more logic to extract Args... from this tuple and pass to(*func.wrapped_method_inside)(....)
(*func.wrapped_method_inside)(ArgsExtractorFromTuple(mytup)); // this part is not implemented yet, but i think it should be possible. This is not my primary concern at the moment
}
//call1
template<typename... Args, typename... Targs, std::size_t... N>
auto call1(std::tuple<Args...> tupA, std::index_sequence<N>..., std::tuple<Targs...> tupT, Targs ..)
{
auto booltup = tuple_creator<0>(tupA, tupT, nullptr); // to create a tuple of bools
auto ret1 = std::make_tuple<Args...>(call2(booltup, targs, N)...); // targs and N are expanded together so that i get indirect access to see the corresponding type in Args...
return ret1;
}
// tuple_creator is a recursive function template with sole purpose to create a boolean tuple.
// such that std::get<0>(booltup) = true,
//if tuple_element_t<0,std::tuple<Args...>> and tuple_element_t<0,std::tuple<targs...>> are same types else false
template<size_t I, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I == sizeof...(targs)>*)
{
return std::make_tuple(std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value);
}
template<size_t I = 0, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I < sizeof...(targs)>*)
{
auto ret1 = tuple_creator<I+1>(tupA, tupT, nullptr);
if(!I)
return ret1;
auto ret2 = std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value;
return std::tuple_cat(ret1, std::make_tuple(ret2));
}
template<typename TT, typename Tuple>
auto call2(Tuple boolyup, TT t, std::size_t I)
{
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
return ret;
}
transform_back is a template that uses a bool template param and enable_if based specialization to decide whether transform an argument back or not
below are the transform_back specialization for std::vector. Similarly i have others for when T = Class etc and so on
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value &&
is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& sameTypes), T>
transform_back(T val) // it was never transfoemd in first place, return as is
{
return val;
}
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value
&& is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& !sameTypes),
typename std::vector<typename T::value_type::element_type>>
transform(T val)
{
std::vector<T::value_type::element_type> t;
for(int i = 0 ; i < val.size(); ++i)
{
typename T::value_type::element_type obj = *val[i];
t.push_back(obj);
}
return t;
}
Both these specialization are same and only differ on sameTypes boolean variable
This code currently errors out in call2 method while trying to using
std::get
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
How can you help?
1)What could be the work around to std::get issue here? Just cant figure out a way to fit in std::size_t as template arg here instead of function arg to make it work at compile time.
Other than this:
2)If you can suggest an alternative approach to implement from top level.
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
That would be great. The path i took is not very convincing personally to me.
If I understand correctly, you might do something like:
template <typename> struct Tag{};
std::shared_ptr<SomeClass> transform_to(Tag<std::shared_ptr<SomeClass>>, const SomeClass& s)
{
return std::make_shared<SomeClass>(s);
}
std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<std::shared_ptr<SomeClass>>>, const std::vector<SomeClass>& v)
{
std::vector<std::shared_ptr<SomeClass>> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(std::make_shared<SomeClass>(s));
}
return res;
}
const SomeClass& transform_to(Tag<SomeClass>, const std::shared_ptr<SomeClass>& s)
{
return *s;
}
std::vector<SomeClass> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}
template <typename T>
const T& transform_to(Tag<T>, const T& t) { return t; } // No transformations
And then
std::function<void (Args...)> func;
template <typename ... transformed_args>
void operator () (transformed_args... targs) const
{
func(transform_to(Tag<Args>(), targs)...);
}
Just explaining the use case here to add some context. Consider these three methods in C++ each represented with the function pointer SomeTemplateClass::func:
void foo(vector<shared_ptr<SomeClass>>) // 1
// Args... = vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>) // 2
// Args... = vector<SomeClass>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>, vector<shared_ptr<SomeClass>>) // 3
// Args... = vector<SomeClass>, vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>, vector<shared_ptr<SomeClass>>
One instance each of SomeTemplateClass is exposed to Python via Pybind. I do these transformations so that when foo is called from Python, any arg vector<T>(in C++) is received as vector<shared_ptr<T>> in SomeTemplateClass functor. This helps in to get handle to previously created objects T that i need.
But as you can see from 3 cases for foo, foo(vector<shared_ptr<T>>) does not need to be transformed to and subsequently not need to be transformed back. The case of 'tranform_to'is easily handled with template specialization, but while transforming back, vector<shared_ptr<T>> cant be blindly converted back to vector<T>. So (transform(targs...)) needs an additional logic to transform a particular arg (or targ) only when targ[i]::type != arg[i]::type
Building on Jarod's answer, i rather need something like this where in transform_to method for vector<shared_ptr> is further divided in two possible templates
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
return v;
}
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<<SomeClass>
transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}

Constructing tuple of value from tuple of reference, with one-argument templated constructor

I have a tuple of const references std::tuple<const Matrix&, ...> from which I construct a tuple of values std::tuple<Matrix, ...>. For any size of tuple greater than 1, this works fine: (online example: https://godbolt.org/g/24E8tU)
#include <tuple>
struct Matrix {
Matrix() = default;
Matrix(Matrix const&) = default;
template <typename T>
explicit Matrix(T const&) {
// in reality, this comes from Eigen, and there is real work
// being done here. this is just to demonstrate that the code
// below fails
static_assert(std::is_same<T, int>::value, "!");
}
};
void works() {
Matrix m1, m2;
std::tuple<const Matrix &, const Matrix &> tuple_of_ref{m1, m2};
std::tuple<Matrix, Matrix> t{tuple_of_ref};
}
However, for a tuple of size 1, this code fails to compile:
void fails() {
Matrix m;
std::tuple<const Matrix &> tuple_of_ref{m};
// Tries and fails to instantiate Matrix(std::tuple<const Matrix &>)
std::tuple<Matrix> t{tuple_of_ref};
}
Note the Matrix class has a templated constructor which accepts std::tuple.
template<typename T>
explicit Matrix(const T& x)
I don't want to use this constructor, and I can't change it since it's third-party code.
I think my works() example properly calls the constructor listed as #4 on cppreference:
template< class... UTypes >
tuple( const tuple<UTypes...>& other );
4) Converting copy-constructor. For all i in sizeof...(UTypes), initializes ith element of the tuple with std::get<i>(other).
The fails() example tries to use this constructor, presumably #3, which I don't want:
template< class... UTypes >
explicit tuple( UTypes&&... args );
3) Converting constructor. Initializes each element of the tuple with the corresponding value in std::forward<Utypes>(args).
How can I make sure tuple's constructor #4 is used for both cases? My real use case is inside a variadic template so I don't know the size of the tuple in advance.
Yeah, so... this is the problem:
template<typename T>
explicit Matrix(const T& x)
That is a really unfriendly constructor - because it's lying. Matrix isn't actually constructible from anything, just some specific things - but there's no way to externally detect what those things are.
When considering what how to construct a tuple<Matrix> from a tuple<Matrix const&>, we have lots of choices, but actually only two are viable:
// #2, with Types... = {Matrix}
tuple(Matrix const&);
// #3, with UTypes = {tuple<Matrix const&>&}
tuple(tuple<Matrix const&>&);
Both end up trying to construct a Matrix from a tuple<Matrix const&>, which doesn't work and you're stuck.
Now, you might think that #4 was your salvation here - generating a constructor that is:
tuple(tuple<Matrix const&> const& );
And constructing its underlying Matrix from the tuple argument's underlying Matrix. That is, using the converting copy constructor. It seems like the problem is that this constructor works but that #3 is preferred for whatever reason (i.e. that it takes a less cv-qualified reference) and that the solution is to try to fiddle with the arguments so that #4 is preferred (i.e. by using as_const() on the argument).
But that constructor isn't less preferred... it's actually not viable here because the restriction on that constructor is (from LWG 2549):
either sizeof...(Types) != 1, or (when Types... expands to T and UTypes... expands to U) is_­convertible_­v<const tuple<U>&, T>, is_constructible_­v<T, const tuple<U>&>, and is_­same_­v<T, U> are all false.
But we do have sizeof..(Types) == 1 and those things are not all false (in particular, the second one is true - which is the source of all your problems to begin with), so #4 is simply not a candidate and there isn't really a neat trick to just make it one.
So, how to fix it? The best thing by far to do would to be fix Matrix. I realize that probably isn't likely, but it has to be said.
You could wrap Matrix in something that actually adds constraints to its constructor to avoid this problem. Since you're already copying it into a tuple, that gives you the opportunity to do something even as simple as:
template <typename T>
struct only_copyable {
only_copyable(only_copyable const& ) = default;
only_copyable(T const& t) : t(t) { }
template <typename U> only_copyable(U const& ) = delete;
T t;
};
but you probably want something little more realistic than this. Also your type could just inherit from Matrix and just fiddle with its constructors for sanity's sake.
Or, when dealing specifically with tuples of size 1, you could avoid using the tuple constructor and just default construct/assign. Or explicitly call get<0> or things of that sort.
Or, you could just avoid tuples of size 1 entirely. Which is a weirdly specific thing to do, but maybe that's enough (or you could wrap tuple in such a way that my_tuple<A, B, C...> is tuple<A,B,C...> but my_tuple<A> is really tuple<A, monostate>).
But really... fixing the Matrix constructor seems just super worthwhile.
Not exactly what you asked but... what about passing through a intermediate template function tplHelper() that, in the more generic case, simply return the value received
template <typename T>
T tplHelper (T const & tpl)
{ return tpl; }
but in case of a std::tuple with a single type return the contained value ?
template <typename T>
T tplHelper (std::tuple<T> const & tpl)
{ return std::get<0>(tpl); }
If you create t passing through tplHelper()
std::tuple<Matrix, Matrix> t{ tplHelper(tuple_of_ref) };
// ...
std::tuple<Matrix> t{ tplHelper(tuple_of_ref) };
when you have two or mote types, you continue to call the copy constructor of std::tuple, but when you call it with a tuple with a single matrix, you avoid the template Matrix constructor and call the copy Matrix constructor.
The following is a full working example
#include <tuple>
struct Matrix
{
Matrix ()
{ }
template <typename T>
explicit Matrix (const T &)
{
// This constructor fails to compile when T is std::tuple<...>
// and I don't want to use it at all
static_assert(sizeof(T) == 0, "!");
}
};
template <typename T>
T tplHelper (T const & tpl)
{ return tpl; }
template <typename T>
T tplHelper (std::tuple<T> const & tpl)
{ return std::get<0>(tpl); }
void m2 ()
{
Matrix m1, m2;
std::tuple<const Matrix &, const Matrix &> tuple_of_ref{m1, m2};
std::tuple<Matrix, Matrix> t{ tplHelper(tuple_of_ref) };
}
void m1 ()
{
Matrix m;
std::tuple<const Matrix &> tuple_of_ref{m};
// now compile!
std::tuple<Matrix> t{ tplHelper(tuple_of_ref) };
}
int main ()
{
m2();
m1();
}
I couldn't think of a good solution to the problem without effectively reimplementing the tuple constructor you wanted to be invoked:
struct TupleFromTuple{};
template<class... T, class... U>
struct TupleFromTuple<std::tuple<T...>, std::tuple<U...>>
{
static_assert(sizeof...(T) == sizeof...(U), "Tuples should be the same size");
using to_t = std::tuple<T...>;
using from_t = std::tuple<U...>;
static to_t Apply(from_t& tup)
{
return ApplyImpl(tup, std::index_sequence_for<T...>{});
}
private:
template<size_t... I>
static to_t ApplyImpl(from_t& tup, std::index_sequence<I...>){
return {std::get<I>(tup)...};
}
};
Demo
Uses some light C++14, but nothing that you cannot implement in C++11
Effectively we use index sequences to call std::get ourselves.
Given some garbage implementation of Matrix like so:
struct Matrix
{
Matrix() = default;
template<class T>
explicit Matrix(const T& foo){foo.fail();}
};
Your fails test now passes:
void fails() {
Matrix m;
std::tuple<const Matrix &> tuple_of_ref{m};
auto t = TupleFromTuple<std::tuple<Matrix>, decltype(tuple_of_ref)>::Apply(tuple_of_ref);
}
I'd circumvent this issue by calling std::get on the "source" tuple:
Thing thing;
std::tuple<Thing const &> get_source{thing};
std::tuple<Thing> get_target{std::get<0>(get_source)};
This avoids calling the explicit constructor and instead calls the copy constructor.
In order to generalize this for tuples of any length you can make use of std::integer_sequence and what builds upon it and wrap this all up in a function:
template<typename T>
using no_ref_cv = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<typename... T, std::size_t... Idx>
auto MakeTupleWithCopies_impl(std::tuple<T...> const & source, std::index_sequence<Idx...>) {
return std::tuple<no_ref_cv<T>...>{std::get<Idx>(source)...};
}
template<typename... T>
auto MakeTupleWithCopies(std::tuple<T...> const & source) {
return MakeTupleWithCopies_impl(source, std::index_sequence_for<T...>{});
}
Stuck with C++11?
std::integer_sequence and it's friends can be written in C++11, too (not a full replacement, misses member function for example and probably breaks with signed integer types):
template<typename T, T...>
struct integer_sequence {};
template<std::size_t... Ints>
using index_sequence = integer_sequence<std::size_t, Ints...>;
template<typename T, T... t>
integer_sequence<T, t..., sizeof...(t)> inc(integer_sequence<T, t...>) {
return {};
}
template<typename T, T N, std::size_t Count>
struct make_integer_sequence_help {
using type = decltype(inc(typename make_integer_sequence_help<T,N,Count - 1>::type{}));
};
template<typename T, T N>
struct make_integer_sequence_help<T, N, 0> {
using type = integer_sequence<T>;
};
template<class T, T N>
using make_integer_sequence = typename make_integer_sequence_help<T,N, N>::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template<class... T>
using index_sequence_for = make_index_sequence<sizeof...(T)>;
Then you only need to change the auto return type specifications of the two functions to std::tuple<no_ref_cv<T>...>.

Base class C++14 template functions are not visible in clang on Mac os ( recursive templates)

This code can be used just fine on gcc , but it does not work on mac OS with clang
The code is utilizing recursive templates via inheritance of next type in the typelist
Think of a Entity => Head and Entities... => Tail...
template<typename ...Entities>
struct GenericBidderCacheLoader;
template<typename Entity, typename ...Entities>
struct GenericBidderCacheLoader<Entity, Entities ...> : GenericBidderCacheLoader<Entities ...> {
using GenericBidderCacheLoader<Entities ...>::retrieve;
template<typename Config>
GenericBidderCacheLoader(const Config &config): GenericBidderCacheLoader<Entities...>(config), entity(config)
{}
void load() noexcept(false) {
entity.load();
GenericBidderCacheLoader<Entities...>::load();
}
template<typename T, typename... Keys>
decltype(std::declval<Entity>().retrieve(std::declval<T&>(),std::declval<Keys>()...), bool())
retrieve(T & t, Keys&& ... keys) {
return entity.template retrieve(t, std::forward<Keys>(keys)...);
}
Entity entity;
};
template<typename Entity>
struct GenericBidderCacheLoader<Entity> {
template<typename Config>
GenericBidderCacheLoader(const Config &config): entity(config)
{}
void load() noexcept(false) {
entity.load();
}
template<typename T, typename... Keys>
decltype(std::declval<Entity>().retrieve(std::declval<T&>(), std::declval<Keys>()...), bool())
retrieve(T & t, Keys&& ... keys) {
return entity.template retrieve(t, std::forward<Keys>(keys)...);
}
Entity entity;
};
Basically GenericBidderCacheLoader holds a typeist , and inheritance model works like that GenericBidderCacheLoad<FirstType> is derived from GenericBidderCacheLoad<SecondType> which is derived from GenericBidderCacheLoad<ThirdType> Which is very common if you want to build a typelist with delegated functionality .
Use case for GenericBidderCacheLoader type is here:
using CacheLoader = vanilla::GenericBidderCacheLoader<RefererEntity<>, ICOCampaignEntity<>, AdDataEntity<BidderConfig>>;
...
...
CacheLoader cacheLoader(config);
//Return from each lambda becomes input for next lambda in the tuple of functions
auto retrieve_referer_f = [&cacheLoader](const std::string& ref, auto&& ...) {
Referer referer;
if(!cacheLoader.retrieve(referer,ref)) {
return boost::optional<uint32_t>();
}
return boost::optional<uint32_t>(referer.ref_id);
};
auto retrieve_ico_campaign_f = [&cacheLoader](boost::optional<uint32_t> ref_id, auto&& ...) {
std::vector<ICOCampaign> ico_campains;
if (!cacheLoader.retrieve(ico_campains,*ref_id)) {
return boost::optional<decltype(ico_campains)>();
}
return boost::optional<decltype(ico_campains)>(ico_campains);
};
auto retrieve_campaign_ads_f = [&cacheLoader](boost::optional<std::vector<ICOCampaign>> campaigns, auto && req, auto && imp) {
std::vector<Ad> retrieved_cached_ads;
for (auto &campaign : *campaigns) {
if (!cacheLoader.retrieve(retrieved_cached_ads, campaign.campaign_id, imp.banner.get().w, imp.banner.get().h)) {
continue;
}
auto budget_bid = selector.authorize(cacheLoader.get_entity<CampaignCache<BidderConfig>>(), campaign.campaign_id);
std::transform(std::begin(retrieved_cached_ads),
std::end(retrieved_cached_ads),
std::begin(retrieved_cached_ads), [budget_bid](Ad & ad){
ad.auth_bid_micros = std::min(budget_bid, ad.max_bid_micros);
return ad;
});
}
if ( retrieved_cached_ads.empty() ) {
return boost::optional<decltype(retrieved_cached_ads)>();
}
return boost::optional<decltype(retrieved_cached_ads)>(retrieved_cached_ads);
};
Error message :
error: no matching member function for call to
'retrieve'
if (!cacheLoader.retrieve(ico_campains,*ref_id)) {
~~~~~~~~~~~~^~~~~~~~
generic_bidder_cache_loader.hpp:42:9: note: candidate template ignored: substitution failure [with
T = std::__1::vector<ICOCampaign, std::__1::allocator<ICOCampaign> >, Keys = <unsigned int &>]: non-const lvalue reference to type 'Referer'
cannot bind to a value of unrelated type 'std::__1::vector<ICOCampaign, std::__1::allocator<ICOCampaign> >'
retrieve(T & t, Keys&& ... keys) {
In case of GCC
using GenericBidderCacheLoader<Entities...>::retrieve
Helped GCC to see those functions defined in all base classes , but clang gives up can't see them ?
Ideally it should work as following , when substitution fails SFINAE it should be able to see function in any of the derived, base-I , base-II , base-III classes.
Full implementation can be found in the temporary branch on github here
https://github.com/venediktov/vanilla-rtb/tree/ico_bidder20171217
https://github.com/venediktov/vanilla-rtb/blob/ico_bidder20171217/examples/ico_campaign_bidder/ico_campaign_bidder_test.cpp
https://github.com/venediktov/vanilla-rtb/blob/ico_bidder20171217/rtb/datacache/generic_bidder_cache_loader.hpp
I also tried to do recursion to help Clang to see those recursive instantiated class functions
template<typename T, typename... Keys>
using retrieve_type = decltype(std::declval<Entity>().retrieve(std::declval<T&>(), std::declval<Keys>()...)) ;
template<typename T, typename... Keys>
typename std::enable_if<std::is_same<retrieve_type<T,Keys...>,bool>::value,bool>::type
retrieve(T & t, Keys&& ... keys) {
return entity.template retrieve(t, std::forward<Keys>(keys)...);
}
template<typename T, typename... Keys>
typename std::enable_if<std::is_same<typename GenericBidderCacheLoader<Entities ...>::template retrieve_type<T,Keys...>,bool>::value,bool>::type
retrieve(T & t, Keys&& ... keys) {
return GenericBidderCacheLoader<Entities ...>::retrieve(t, std::forward<Keys>(keys)...);
}
But it only helped clang to see the second in the typelist but not the third , it almost the case that I had to recurse to the final type from the top level .
This question lacks a minimal, complete, and verifiable example. So I haven't tested OP's code. However, by reading the quoted code piece and the error message, I constructed a minimal example which can make the different behavior between g++ and clang++ clearer:
#include <utility>
#include <type_traits>
template<class U>
struct Foo
{
template<class V>
decltype(std::declval<U>().echo_dumb(std::declval<V>()))
dumb(V const &v) {
u.echo_dumb(v);
}
U u;
};
template<class T, class U>
struct Bar
: public Foo<U>
{
using Foo<U>::dumb;
template<class V>
decltype(std::declval<T>().echo_dumb(std::declval<V>()))
dumb(V const &v) {
t.echo_dumb(v);
}
T t;
};
struct A { void echo_dumb(A const &) {} };
struct B { void echo_dumb(B const &) {} };
int main() {
Bar<A, B> bar;
bar.dumb(A{}); // ok for everyone
bar.dumb(B{}); // error for clang++, ok for g++
}
Thus the problem becomes "what is the semantics of using declaration ?".
After reading some C++ standard pages, I think clang++ has correctly rejected this code, because:
[namespace.udecl]
The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.
...
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.
Since 2 dumb function have exactly the same parameter-type-list, the one introduced by using declaration is hidden and will not be included.
If the dumb in Foo has signature dumb(V const &v, int = 0), then both g++ and clang++ accept this code.
What is interesting is, if we replace 2 decltype(std::declval<U>().echo_dumb(std::declval<V>())) by simply a void, then both g++ and clang++ correctly reject it, with message no matching function. So it seems the decltype part in the return part confuses g++ compiler.
This is not an answer , I just wanted to show how I fixed my code based on the answer from #liliscent
template<typename T, typename... Keys>
bool retrieve(T & t, Keys&& ... keys) {
auto tuple = std::make_tuple(std::forward<Keys>(keys)...);
return this->retrieve(t, tuple, std::make_index_sequence<std::tuple_size<decltype(tuple)>::value>());
}
template<typename T, typename Tuple, std::size_t... Idx>
decltype(std::declval<Entity>().retrieve(std::declval<T&>(),std::get<Idx>(std::declval<Tuple>())...), bool())
retrieve(T & t, Tuple&& tuple, std::index_sequence<Idx...>, Entity* = 0) {
return entity.template retrieve(t, std::get<Idx>(std::forward<Tuple>(tuple))...);
}
New code now compiles with both gcc and clang . I would be happy If anyone reviewed my change. This code is an open-source project available on GitHub. The links to the files in my original question are outdated if anyone wants to see the original problem please click on revision history.
And with sizeof... operator it's even shorter
template<typename T, typename... Keys>
bool retrieve(T & t, Keys&& ... keys) {
return this->retrieve(t, std::make_tuple(std::forward<Keys>(keys)...),
std::make_index_sequence<sizeof...(keys)>()
);
}
template<typename T, typename Tuple, std::size_t... Idx>
decltype(std::declval<Entity>().retrieve(std::declval<T&>(),std::get<Idx>(std::declval<Tuple>())...), bool())
retrieve(T & t, Tuple&& tuple, std::index_sequence<Idx...>, Entity* = 0) {
return entity.template retrieve(t, std::get<Idx>(std::forward<Tuple>(tuple))...);
}

Is there a way to cast Template's Type to shared_ptr<T>?

Recently, I am working for a school's assignment which is about to construct a very simple polynomial expression by using class and object.
We don't have to construct parse function , so to construct an normal expression there's a lot codes needed to write and very hard to discern when there's many codes. So I thought that maybe this is a good context to try C++ template ( I'm really a fresh man for C++, so I am not so experienced for template and not sure if in this condition I could use it.)
as an example, I need to implement OperatorPlus whose declaration correspondant isstd::shared<Expression> OperateurPlus(std::shared<Expression>, std::shared<Expression>). I want to create template<typename T, typename M> std::shared<Expression> plus(T lhs, M rhs) wrapper to response different passed-in parameters. And I follow the where clause in another language to add the type limitation by utilizing enable_if. So the code is like this:
template<typename T, typename M,
typename = std::enable_if<(
std::is_same<unsigned int, T>::value ||
std::is_same<char, T>::value ||
std::is_same<std::shared_ptr<Expression>, T>::value) &&
( std::is_same<unsigned int, M>::value ||
std::is_same<std::shared_ptr<Expression>, M>::value ||
std::is_same<char, M>::value)>
>
std::shared_ptr<Expression> plus(T lhs, M rhs){
std::shared_ptr<Expression> t_lhs, t_rhs;
if (std::is_same<T, uint>::value) t_lhs = Nombre::create(uint(lhs));
if (std::is_same<T, char>::value) t_lhs = std::shared_ptr<Expression>(new Variable(char(lhs)));
if (std::is_same<T, std::shared_ptr<Expression>>::value) t_lhs = (std::shared_ptr<Expression>)(lhs);
if (std::is_same<M, uint>::value) t_rhs = Nombre::create(uint(rhs));
if (std::is_same<M, char>::value) t_rhs = std::shared_ptr<Expression>(new Variable(char(rhs)));
if (std::is_same<M, std::shared_ptr<Expression>>::value) t_rhs = (std::shared_ptr<Expression>)(rhs);
return std::shared_ptr<Expression>(new OperateurPlus(t_lhs, t_rhs));
}
and my question is (std::shared_ptr<Expression>)(lhs) this part. I used a c-style casting because I don't how to realize this casting operation. IDE told my std::shared_ptr is not a pointer or reference if I tried static_cast> and it think lhs is unsigned int type.
So if I just follow the compile's hints, my question is
how to cast a template's Type to std::shared_ptr? or
If is it possible to pass a std::shared_ptr as a template parameter?
The body of a template function must be entirely compilable for a given template instantiation. Even if one of those if statements will never be visited, the condition still needs to be syntactically valid for the appropriate types.
Having a single function for this is the wrong approach. One possible solution would be to create an overloaded function to get a std::shared_ptr from any of your required sources, then use that function to achieve the desired genericicity.
using ExpPtr = std::shared_ptr<Expression>; //for brevity
ExpPtr convertToExp (ExpPtr e) {
return e;
}
ExpPtr convertToExp (unsigned int i) {
return Nombre::create(i);
}
ExpPtr convertToExp (char c) {
return std::make_shared<Variable>(c);
}
template <typename T, typename U>
ExpPtr plus (T lhs, U rhs) {
auto lhsExp = convertToExp(lhs);
auto rhsExp = convertToExp(rhs);
return std::make_shared<OperateurPlus>(lhsExp, rhsExp);
}
I don't think all the SFINAE is necessary. This will hard-fail if there isn't a valid call to convertToExp for T or U.
I didn't try to compile this as you didn't supply an MVCE, so there might be some mistakes.
If you really want the SFINAE to prevent implicit conversions in the convertToExp calls, you could do it in a cleaner way like this:
template<typename... Conds>
struct or_ : std::false_type {};
template<typename Cond, typename... Conds>
struct or_<Cond, Conds...>
: std::conditional_t<Cond::value, std::true_type,
or_<Conds...>> {};
template <typename T, typename... Ts>
using is_one_of = or_<std::is_same<T,Ts>...>;
template <typename T>
using is_valid_exp_source =
is_one_of<T, char, unsigned int, std::shared_ptr<Expression>>;
template <typename T, typename U>
std::enable_if_t<is_valid_exp_source<T>::value && is_valid_exp_source<U>::value,
ExpPtr>
plus (T lhs, U rhs) {
auto lhsExp = convertToExp(lhs);
auto rhsExp = convertToExp(rhs);
return std::make_shared<OperateurPlus>(lhsExp, rhsExp);
}