I've encountered strange behavior when trying to construct a tuple of references from a mix of tuples and integral values.
Given the following:
struct A { int v = 1; };
struct B { int v = 2; };
struct C { int v = 3; };
A a;
std::tuple<B,C> tpl;
I'm trying to create a third tuple which holds references to all instances, so that each instance's v will be assignable and readable through it.
Seems simple enough using templates
template <class Tuple, size_t... Is>
constexpr auto as_ref_impl(Tuple t, std::index_sequence<Is...>) {
return std::tuple_cat(std::tie(std::get<Is>(t))...);
// or
// return std::make_tuple(std::ref(std::get<Is>(t))...);
}
template <class...Args>
constexpr auto as_ref(std::tuple<Args...>& t) {
return as_ref_impl(t, std::index_sequence_for<Args...>{});
}
and then
auto ref_tpl = std::tuple_cat(std::tie(a), as_ref(tpl));
which builds fine (in both versions).
Unfortunately only the parts of the reference tuple (ref_tpl), which originate from integral values, can be assigned or read from successfully.
I'm using C++14 and gcc 9.3.0.
Any ideas, or insight why this does not work, are very welcome!
Minimal working example:
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
#include <functional>
struct A { int v = 1; };
struct B { int v = 2; };
struct C { int v = 3; };
A a;
std::tuple<B,C> tpl;
template <class Tuple, size_t... Is>
constexpr auto as_ref_impl(Tuple t, std::index_sequence<Is...>) {
//return std::tuple_cat(std::tie(std::get<Is>(t))...);
return std::make_tuple(std::ref(std::get<Is>(t))...);
}
template <class...Args>
constexpr auto as_ref(std::tuple<Args...>& t) {
return as_ref_impl(t, std::index_sequence_for<Args...>{});
}
int main() {
using std::cout;
auto ref_tpl = std::tuple_cat(std::tie(a), as_ref(tpl));
// prints 1 2 3, as expected.
cout << a.v << std::get<0>(tpl).v << std::get<1>(tpl).v << std::endl;
std::get<0>(ref_tpl).v = 8; // works
std::get<1>(ref_tpl).v = 9; // does not work
std::get<2>(ref_tpl).v = 10; // does not work
// should output 8 9 10 instead outputs 8 2 3
cout << a.v << std::get<0>(tpl).v << std::get<1>(tpl).v << std::endl;
// should output 8 9 10, instead outputs garbage.
cout << std::get<0>(ref_tpl).v << std::get<1>(ref_tpl).v << std::get<2>(ref_tpl).v << std::endl;
return 0;
}
This is a simple typo:
constexpr auto as_ref_impl(Tuple t, std::index_sequence<Is...>) {
Tuple is taken by value, so a local copy is made, and the reference is made relative to it.
You should take Tuple by reference instead,
constexpr auto as_ref_impl(Tuple& t, std::index_sequence<Is...>) {
Your as_ref_impl needs to take the Tuple parameter by reference, otherwise you are taking a std::ref to a function local. This explains the unmodified values of tpl, and the garbage values in ref_tpl.
Do this instead:
template <class Tuple, size_t... Is>
// note the reference parameter
constexpr auto as_ref_impl(Tuple &t, std::index_sequence<Is...>) {
return std::make_tuple(std::ref(std::get<Is>(t))...);
}
Here's a demo.
Related
I'm using C++17. I'd like to get an element of a tuple that satisfies some type trait. It would be amazing if the trait could be supplied generically, but I'd be satisfied with a specific function for a certain trait. Usage might look something like this:
auto my_tuple = std::make_tuple { 0.f, 1 };
auto basic = get_if_integral (my_tuple);
auto fancy = get_if<std::is_floating_point> (my_tuple);
std::cout << basic; // '1'
std::cout << fancy; // '0.f'
Ideally this would fail to compile if more than one element satisfies the trait, like std::get (std::tuple).
Here's a surprisingly simple way without using recursion:
template <template <typename...> typename T, typename... Ts>
constexpr int index_of_integral(const T<Ts...>&)
{
const bool a[] = { std::is_integral_v<Ts>... };
for (int i = 0; i < sizeof...(Ts); ++i) if (a[i]) return i;
return -1;
}
template <typename T>
constexpr decltype(auto) get_if_integral(T&& t)
{
return std::get<index_of_integral(t)>(std::forward<T>(t));
}
int main()
{
constexpr auto t = std::make_tuple(3.14, 42, "xyzzy");
static_assert(get_if_integral(t) == 42);
}
It could easily be extended to be parametrized on the trait.
The only things that make it C++17 are the is_integral_v variable template and the single-argument static_assert. Everything else is C++14.
Note that in C++20 the for loop could be replaced with std::find and std::distance.
Ideally it should throw an exception instead of returning -1, but compilers don't seem to like that.
Inspired by this answer.
If I understand correctly what you want... I propose an helper struct gf_h ("get first helper") as follows
template <std::size_t, bool ...>
struct gf_h
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, false, Bs...> : public gf_h<I+1u, Bs...>
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, true, Bs...> : public std::integral_constant<std::size_t, I>
{ };
and a couple of functions that use it:
template <typename ... Us,
std::size_t I = gf_h<0, std::is_integral<Us>::value...>::value>
auto get_first_integral (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
template <typename ... Us,
std::size_t I = gf_h<0, std::is_floating_point<Us>::value...>::value>
auto get_first_floating (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
Observe that are SFINAE enabled/disabled functions, so are enabled only if there is an integral (or float) value in the tuple
The following is a full compiling example
#include <tuple>
#include <iostream>
template <std::size_t, bool ...>
struct gf_h
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, false, Bs...> : public gf_h<I+1u, Bs...>
{ };
template <std::size_t I, bool ... Bs>
struct gf_h<I, true, Bs...> : public std::integral_constant<std::size_t, I>
{ };
template <typename ... Us,
std::size_t I = gf_h<0, std::is_integral<Us>::value...>::value>
auto get_first_integral (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
template <typename ... Us,
std::size_t I = gf_h<0, std::is_floating_point<Us>::value...>::value>
auto get_first_floating (std::tuple<Us...> const & t)
{ return std::get<I>(t); }
int main()
{
auto tup1 = std::make_tuple(3.f, 2., 1, 0);
std::cout << get_first_integral(tup1) << std::endl; // 1
std::cout << get_first_floating(tup1) << std::endl; // 3
auto tup2 = std::make_tuple("abc", 4, 5);
std::cout << get_first_integral(tup2) << std::endl; // 4
// std::cout << get_first_floating(tup2) << std::endl; // error
auto tup3 = std::make_tuple("xyz", 6., 7.f);
// std::cout << get_first_integral(tup3) << std::endl; // error
std::cout << get_first_floating(tup3) << std::endl; // 6
}
Ok, I figured out a way to accomplish this in a way that is not generic over the trait, but that's good enough for my current purpose. Using if constexpr this really doesn't look too bad. I'm sure this isn't hugely idiomatic, but it works for me:
template <std::size_t Idx, typename... Us>
auto& get_if_integral_impl (std::tuple<Us...>& t)
{
static_assert (Idx < std::tuple_size_v<std::tuple<Us...>>,
"No integral elements in this tuple.");
if constexpr (std::is_integral<std::tuple_element_t<Idx, std::tuple<Us...>>>::value)
return std::get<Idx> (t);
else
return get_if_integral_impl<Idx + 1> (t);
}
template<typename... Us>
auto& get_if_integral (std::tuple<Us...>& t)
{
return get_if_integral_impl<0> (t);
}
auto tup = std::make_tuple (3.f, 2., 1, 0);
std::cout << get_if_integral (tup); // '1'
My use case is a little more complex, involving returning the first nested tuple which itself contains another type, but this should convey the basic idea.
I have been writing a set of classes to allow for a simple python-like zip-function. The following snippet works (almost) just as expected. However, the two variables a and b are not const.
std::vector<double> v1{0.0, 1.1, 2.2, 3.3};
std::vector<int> v2{0, 1, 2};
for (auto const& [a, b] : zip(v1, v2))
{
std::cout << a << '\t' << b << std::endl;
a = 3; // I expected this to give a compiler error, but it does not
std::cout << a << '\t' << b << std::endl;
}
I have been using gcc 7.3.0.
Here is the MCVE:
#include <iostream>
#include <tuple>
#include <vector>
template <class ... Ts>
class zip_iterator
{
using value_iterator_type = std::tuple<decltype( std::begin(std::declval<Ts>()))...>;
using value_type = std::tuple<decltype(*std::begin(std::declval<Ts>()))...>;
using Indices = std::make_index_sequence<sizeof...(Ts)>;
value_iterator_type i;
template <std::size_t ... I>
value_type dereference(std::index_sequence<I...>)
{
return value_type{*std::get<I>(i) ...};
}
public:
zip_iterator(value_iterator_type it) : i(it) {}
value_type operator*()
{
return dereference(Indices{});
}
};
template <class ... Ts>
class zipper
{
using Indices = std::make_index_sequence<sizeof...(Ts)>;
std::tuple<Ts& ...> values;
template <std::size_t ... I>
zip_iterator<Ts& ...> beginner(std::index_sequence<I...>)
{
return std::make_tuple(std::begin(std::get<I>(values)) ...);
}
public:
zipper(Ts& ... args) : values{args...} {}
zip_iterator<Ts& ...> begin()
{
return beginner(Indices{});
}
};
template <class ... Ts>
zipper<Ts& ...> zip(Ts& ... args)
{
return {args...};
}
int main()
{
std::vector<double> v{1};
auto const& [a] = *zip(v).begin();
std::cout << a << std::endl;
a = 2; // I expected this to give a compiler error, but it does not
std::cout << a << std::endl;
}
You have a tuple of a reference, which means that the reference itself will be const qualified (which is ill-formed but in this context ignored), not the value referenced by it.
int a = 7;
std::tuple<int&> tuple = a;
const auto&[aa] = tuple;
aa = 9; // ok
If you look how std::get is defined, you'll see that it returns const std::tuple_element<0, std::tuple<int&>>& for the structured binding above. As the first tuple element is a reference, the const& has no effect, and thus you can modify the return value.
Really, it's same thing if you have a class pointer/reference member that you can modify in a const qualified member function (the value pointed/referenced that is).
My Problem is the following. I want to sort a list of types based on a list of constexpr values. The problem can be boiled down to this function:
template <typename U, typename V>
auto min(U,V) -> std::conditional_t<U::value < V::value, U, V>
{ return {}; }
whereas value must be some static constexpr member of each type, respecively.
The following snippet demonstrates the usage:
// (I)
// This must even be declared outside of a function body due to the statics :(
struct X { static constexpr double value = 2.; };
struct Y { static constexpr double value = 1.; };
int main()
{
X x;
Y y;
auto z = min(x,y);
std::cout << typeid(z).name() << " : " << z.value << std::endl;
}
My goal is to provide the value as I call the function. The closest thing I got to this goal is
the following
template <double (*F)()>
struct Value { static constexpr double value = F(); };
which can be called like this using lambdas:
// (II)
auto w = min(Value<[]{ return 3.14; }>{}, Value<[]{ return 2.71; }>{});
std::cout << typeid(w).name() << " : " << w.value << std::endl;
The actual type to be sorted can be an additional parameter.
The problem is that the above is not valid C++ according to the standard. However, the latest clang does compile
this gracefully.
Now, my question is: Is there another standard compliant way to achieve the above (listing (II)), that is, defining a function that
computes a type based on constexor objects provided inplace (in some way) as the function argument?
P.S.: I'm aware of the solution using std::integral_constant. This, however, is limited to integral types only. I'm interested in a solution that works for all constexpr objects, in particular floating point types, and strings.
Edit:
To deal with floating point values as well as integral types scenarios you could make use of user defined literal template e.g.:
#include <type_traits>
#include <utility>
#include <typeinfo>
#include <iostream>
template <class FloatingPointType, class... Cs>
constexpr FloatingPointType char_list_to_(Cs... cs) {
char arr[] = {cs...};
FloatingPointType lhs = 0;
bool contains_dot = false;
for (std::size_t i = 0; i < sizeof...(Cs) && !(contains_dot |= (arr[i] == '.')); i++) {
lhs *= 10;
lhs += arr[i] - '0';
}
FloatingPointType rhs = 0;
for (int i = sizeof...(Cs) - 1; i > 0 && arr[i] != '.'; i--) {
rhs /= 10;
rhs += arr[i] - '0';
}
rhs /= 10;
return (contains_dot)?lhs+rhs:lhs;
}
template <class FloatingPointType, char... Cs>
struct FloatingPointValue {
static constexpr FloatingPointType value = char_list_to_<FloatingPointType>(Cs...);
constexpr operator FloatingPointType() {
return value;
}
};
template <class FloatingPointType, char... Cs>
constexpr FloatingPointType FloatingPointValue<FloatingPointType, Cs...>::value;
template <char... Cs>
FloatingPointValue<double, Cs...> operator""_fv() {
return {};
}
template <typename U, typename V>
auto min(U,V) -> std::conditional_t<(U{}<V{}), U, V>
{ return {}; }
int main() {
auto w = min(3.14_fv, 2.71_fv);
std::cout << typeid(w).name() << " : " << w.value << std::endl;
}
Output:
18FloatingPointValueIdJLc50ELc46ELc55ELc49EEE : 2.71
Output of c++filt -t 18FloatingPointValueIdJLc50ELc46ELc55ELc49EEE:
FloatingPointValue<double, (char)50, (char)46, (char)55, (char)49>
[live demo]
But if you wish to apply the same to string literal there is currently a lack of support of the feature caused by a c++ standard. There is however a gnu extension supported by clang and gcc if you are capable to accept less portable option:
#include <type_traits>
#include <utility>
#include <typeinfo>
#include <iostream>
template <class CharT, CharT... Cs>
struct Value {
static constexpr std::size_t size = sizeof...(Cs);
static constexpr CharT const value[sizeof...(Cs) + 1] = {Cs..., '\0'};
template <class RHS>
constexpr bool operator<(RHS) {
for (std::size_t i = 0; i < size && i < RHS::size; i++) {
if (value[i] != RHS::value[i]) {
return value[i] < RHS::value[i];
}
}
return size < RHS::size;
}
};
template <class CharT, CharT... Cs>
constexpr CharT const Value<CharT, Cs...>::value[sizeof...(Cs) + 1];
template <class CharT, CharT... Cs>
Value<CharT, Cs...> operator""_v() {
return {};
}
template <typename U, typename V>
auto min(U,V) -> std::conditional_t<(U{}<V{}), U, V>
{ return {}; }
int main() {
auto w = min("cde"_v, "abc"_v);
std::cout << typeid(w).name() << " : " << w.value << std::endl;
}
Output:
5ValueIcJLc97ELc98ELc99EEE : abc
Output of c++filt -t 5ValueIcJLc97ELc98ELc99EEE:
Value<char, (char)97, (char)98, (char)99>
[live demo]
Thanks to SO guys I resolved one of my problem:
Create a tuple with variatic type wrapped
But I realized after that, I still have a problem that I can't solve.
So now I have :
template < typename T,
size_t Size >
struct Metadata {
using type = T;
std::bitset<Size> bitset;
};
template <class... Ts>
constexpr auto make_metadata()
{
constexpr size_t N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{0}...);
}
I intent to use it "like" that :
constexpr auto result = make_metadata<Foo1, Foo2, Foo3>({0}, {0}, {0});
And according to Jarod42 comment, I think I'll need 2 functions.
But how I can pass the arguments to the function and then to the tuple?
And I wonder how do that but without force to pass each arguments for each Ts, if they are not present I'll just put a default value (2 questions).
this may be what you want, I am not sure. (since you never show where the arguments to be used)
#include <tuple>
#include <bitset>
#include <iostream>
struct A{int value;};
struct B{int value;};
struct C{int value;};
template <typename T,int Size>
struct Metadata{
using type = T;
T value;
std::bitset<Size> bitset;
};
template <typename...Ts,typename...Args>
constexpr auto make_metadata(Args... args)
{
constexpr auto N = sizeof...(Ts);
return std::make_tuple(Metadata<Ts, N>{args,0}...);
}
int main(){
auto data = make_metadata<A,B,C>(1,2,3);
std::cout << "(" << std::get<0>(data).value.value
<< ", " << std::get<1>(data).value.value
<< ", " << std::get<2>(data).value.value << ")";
}
Suppose we have code like this. It works well and pre-calculate first 5 Fibonacci numbers.
#include <iostream>
template <int T>
struct fib;
template <>
struct fib<0>{
constexpr static int value = 1;
};
template <>
struct fib<1>{
constexpr static int value = 1;
};
template <int I>
struct fib{
constexpr static int value = fib<I - 1>::value + fib<I - 2>::value;
};
int main(){
std::cout << fib<0>::value << std::endl;
std::cout << fib<1>::value << std::endl;
std::cout << fib<2>::value << std::endl;
std::cout << fib<3>::value << std::endl;
std::cout << fib<4>::value << std::endl;
std::cout << fib<5>::value << std::endl;
}
However there is "small" problem with it.
What if we need to use this for values, that are not known at compile time?
For few values we can do this:
const int max = 5;
int getData(){
return 5; // return value between 0 and max.
}
int something(){
switch(getData()){
case 0: return fib<0>::value;
case 1: return fib<1>::value;
case 2: return fib<2>::value;
case 3: return fib<3>::value;
case 4: return fib<4>::value;
case 5: return fib<5>::value;
}
}
This will works OK for 5 values, but what if we have 150 or 300?
Is not really serious to change the code with 300 rows...
What could be the workaround here?
If you need to use a value at runtime that isn't known at compile time, you can't compute it at compile time. Obvious.
But... if you can impose a top value to values needed, you can compute all values (from zero to top) at compile time and store them in an std::array.
In the following example I have modified your fib structs (to use a std::size_t index and a template type (with default unsigned long) for the value) and I have added a templated struct fibVals that contain an std::array that is initialized using fib<n>::value
The following main() show that is possible to define a constexpr fibvals<N> (with N == 20 in the example) to compute (at compile time) all fib<n> values in range [0,N[.
#include <array>
#include <utility>
#include <iostream>
template <std::size_t, typename T = unsigned long>
struct fib;
template <typename T>
struct fib<0U, T>
{ constexpr static T value { T(1) }; };
template <typename T>
struct fib<1U, T>
{ constexpr static T value { T(1) }; };
template <std::size_t I, typename T>
struct fib
{ constexpr static T value { fib<I-1U>::value + fib<I-2U>::value }; };
template <std::size_t I, typename T = unsigned long>
struct fibVals
{
const std::array<T, I> vals;
template <std::size_t ... Is>
constexpr fibVals ( std::index_sequence<Is...> const & )
: vals { { fib<Is, T>::value ... } }
{ }
constexpr fibVals () : fibVals { std::make_index_sequence<I> { } }
{ }
};
int main()
{
constexpr fibVals<20> fv;
for ( auto ui = 0U ; ui < fv.vals.size() ; ++ui )
std::cout << "fib(" << ui << ") = " << fv.vals[ui] << std::endl;
}
Unfortunately this example use std::make_index_sequence<I> and std::index_sequence<Is...> that are C++14 features.
If you want implement struct fibVals in C++11, you can implement the following structs struct indexSeq and struct indexSeqHelper, to substitute std::index_sequence<Is...> and std::make_index_sequence<I>
template <std::size_t ...>
struct indexSeq
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSeqHelper
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; };
template <std::size_t ... Next >
struct indexSeqHelper<0U, Next ... >
{ using type = indexSeq<Next ... >; };
and implement fibVals constructors as follows
template <std::size_t ... Is>
constexpr fibVals ( indexSeq<Is...> const & )
: vals { { fib<Is, T>::value ... } }
{ }
constexpr fibVals () : fibVals { typename indexSeqHelper<I>::type { } }
{ }
Templates are evaluated at compile time, so there is no solution with templates that works at runtime.
You can make a constexpr function, which may be evaluated at compile time, depending on the value passed. Obviously, a runtime value may not be computed at compile time, as it is not known at compile time.