C++ Metaprogramming – Tuple

In this article I’m going to implement a custom Tuple implementation inspired by https://medium.com/@mortificador/implementing-std-tuple-in-c-17-3cc5c6da7277.

Custom tuple-like structures are useful when implementing DSL on top of C++.

Implementation Example

// Tuple element structure composed of index _index and type T
template<std::size_t _index, typename T>
class _tuple_impl {
    T val;
public:
    _tuple_impl() {}

    _tuple_impl(const T &t) {
        val = t;
    }

    _tuple_impl(T &&t) {
        val = std::move(t);
    }

    T &get() {
        return val;
    }

};

// Recurrent tuple structure reusing tuple impl structure defined above.
// The idea is to iterate over all elements T and while doing that increment index.
// During recursion, for every variadic template parameter, a  _tuple_impl type
// with custom index is going to be defined.
template<std::size_t _index,
        typename L,
        typename ... types>
class _tuple_recurr_base : public _tuple_impl<_index, typename std::remove_reference<L>::type>,
                           public _tuple_recurr_base<_index + 1, types...> {

};

// Create specialization of recurrent structure, to end recursion
template<std::size_t _index, typename L>
class _tuple_recurr_base<_index, L> : public _tuple_impl<_index, typename std::remove_reference<L>::type> {
};


// Create public tuple hiding index argument
template<typename ... ARGS>
class my_tuple : public _tuple_recurr_base<0, ARGS...> {

};

// Create a type which can through recursion extract type at index T from
// variadic template arguments
template<std::size_t _index, typename L, typename ... ARGS>
struct _get_type : public _get_type<_index - 1, ARGS...> {
};
template<typename L, typename ... ARGS>
struct _get_type<0, L, ARGS...> {
    typedef L type;
};

// Specialization of struct for getting type - to stop recursion
template<typename L>
struct _get_type<0, L> {
    typedef L type;
};


// Function which extracts value from tuple at index
template<std::size_t _index, typename ... types>
typename _get_type<_index, types...>::type & get_at(my_tuple<types...> &t) {
    // Doing static cast to reference, otherwise a sliced copy is going to be made
    return static_cast<_tuple_impl<_index, typename _get_type<_index, types...>::type>&>(t).get();
}


// Example of usage

TEST_CASE("A") {
    my_tuple<int, int> i;

    get_at<0>(i) = 1;
    get_at<1>(i) = 2;

    REQUIRE(get_at<1>(i) == 2);
    REQUIRE(get_at<0>(i) == 1);
}

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *