Making a type "tuple_like"

closed account (3hM2Nwbp)
There's quite a bit of code to give a context of my situation that can be found at https://bitbucket.org/DarkHeart/opengl

However, the problem can be reduced to the following (with some undefined behavior, seemingly...):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <array>
#include <tuple>
#include <string>

template<typename T, std::size_t N>
class Tuple
{
	
};

// If I'm reading the standard correctly, below should produce undefined behavior
// I've tried to read the source of <tuple> to determine what I need to do, but...it's getting dangerous.

template<typename T, std::size_t N>
struct std::tuple_size<Tuple<T, N>> : std::integral_constant<std::size_t, N> {};

template<std::size_t I, typename T, std::size_t N>
struct std::tuple_element<I, Tuple<T, N>> { typedef T type;};

namespace std
{
	template<std::size_t I, typename T, std::size_t N>
	constexpr const T& get(const Tuple<T, N>& tuple) noexcept
	{
		return tuple[I];
	}
	
	template<std::size_t I, typename T, std::size_t N>
	constexpr const T&& get(const Tuple<T, N>&& tuple) noexcept
	{
		return tuple[I];
	}
	
	template<std::size_t I, typename T, std::size_t N>
	constexpr T& get(Tuple<T, N>& tuple) noexcept
	{
		return tuple[I];
	}
	
  template<typename _Tp, std::size_t _Nm>
    struct __is_tuple_like<Tuple<_Tp, _Nm>> : true_type
    {typedef true_type type; };
}

int main()
{
	std::tuple<int, int, int> t0, t1, t2;
	std::tuple_cat(t0, t1, t2);
	
	// How to make my type compatible with std::tuple_cat?
	Tuple<int, 3> t3, t4, t5;
	std::tuple_cat(t3, t4, t5);
}


How might I make my type compatible with std::tuple_cat, if possible? What I am generally aiming for is mating my 3D abstractions with the STL in order to provide a safe, intuitive way to bridge the gap between abstraction and the raw data required by the C bindings of opengl in such a way that errors will be evident, where possible, at compile-time.

Thanks!
Last edited on
std::array<> can be used as a std::tuple<>

1
2
3
4
5
6
7
8
9
10
11
#include <array>
#include <tuple>
#include <iostream>

int main()
{
    std::array<int,3> a{{0,1,2}}, b{{3,4,5}}, c{{6,7,8}};

    auto tup = std::tuple_cat(a,b,c) ;
    std::cout << std::tuple_size< decltype(tup) >::value << '\n' ; // 9
}

http://coliru.stacked-crooked.com/a/e7ed0c7d39b89609
closed account (3hM2Nwbp)
Indeed, std::array may be used, however I would like to make my own types abide by the requirements.

What I want to avoid is:

1
2
3
4
5
6
7
class Point
{
  std::array<...> asArray();
};

Point p1, p2, p3;
std::tuple_cat(p1.asArray(), p2.asArray(), p3.asArray());


in favor of

1
2
3
4
5
6
class Point
{
};

Point p1, p2, p3;
std::tuple_cat(p1, p2, p3);


It's occurred to me that I might be overthinking this...I'm going to see how implicit type conversion would work out with this.
Last edited on
closed account (3hM2Nwbp)
1
2
3
4
5
6
7
8
9
10
11
template<typename T, std::size_t N>
class Point
{
	operator std::array<T, N>()
	{
		return this->elements;
	}
};
Point<int, 3> p1, p2, p3;

std::tuple_cat(p1, p2, p3); // implicit type conversion doesn't happen...any suggestions? 


Well, figured something out...not sure how happy I am with it yet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <array>
#include <cstdint>
#include <tuple>

template<typename T, std::size_t N>
struct MyTuple
{
	
	std::array<T, N> elements;
	
	operator std::array<T, N>()
	{
		return elements;
	}
};

namespace detail
{

	/// Hide the explicit cast from user-code
	template<typename T, std::size_t N, typename... E>
	inline void tuple_cat(std::array<T, N> first, E&&... remaining)
	{
		std::tuple_cat(first, static_cast<std::array<T, N>>(remaining)...);
	}
}

int main()
{
	MyTuple<float, 4> t1, t2, t3;
	/// Now to come up with a clever way to hide the explicit template instantiation
	detail::tuple_cat<float, 4>(t1, t2, t3);
}


Can you see any areas that could be improved?
Last edited on
Is there a compelling reason why std::tuple_cat() must be used?
Why can't you use point_cat() instead?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <tuple>
#include <array>
#include <iostream>

template < typename T = double, std::size_t N = 3 >
struct point
{
    using array_type = std::array<T,N> ;
    array_type array ;
    constexpr point( array_type a ) : array(a) {}
};

template < typename... POINT_TYPE >
auto point_cat( POINT_TYPE&&... p ) -> decltype( std::tuple_cat(p.array...) )
{ return std::tuple_cat(p.array...) ; }

int main()
{
    point<> a { {{0,1,2}} } ;
    point<int,4> b { {{3,4,5,6}} } ;
    point<float,5> c{ {{7,8,9,10,11}} };

    auto tup = point_cat(a,b,c);

    std::cout << std::tuple_size< decltype(tup) >::value << '\n' ; // 12
}

http://coliru.stacked-crooked.com/a/4681ae2795f83c32
closed account (3hM2Nwbp)
I think I edit-ninja'd that reply....but I really like your approach better than the one I came up with!

Anything would work so long as the conversion process from a list of points to a tightly packed array of vertex data is hidden from the end-user.

Thanks!
Last edited on
Topic archived. No new replies allowed.