Search code examples
c++c++11templatesstlinitializer-list

Compile time creation of class member stl container (const std::array) filled with elements


I tried to create a minimal example as it is possible with templates.

(MSVC15, c++14)

There are 2 questions may be connected with each other.

Question 1: Is it possible to create at compile time container class member (for example std::array) filled with elements?

    _limited_int_ptr_container _intIndexes
    {
        // _startIntIndex
        // create indexes???
        std::make_shared<_limited_int_>(_startIntIndex), // 10060u  _startStrIndex
        //std::make_shared<_limited_int_>(_startIntIndex + 1),
        //std::make_shared<_limited_int_>(_startIntIndex + 2),
        //...
        std::make_shared<_limited_int_>(_startIntIndex + _maxIntIndexes -1)
    };

Question 2: Is it possible to combine several template classes with one general template parameter but only once?

    template <class T, size_t Size>
    using _container = const std::array<T, Size>;

    template <class T>
    using _ptr = std::shared_ptr<T>;

    // is it possible to combine this 2 types and use _maxStrIndexes only once?
    using _limited_str_ = IndexStr<_maxStrIndexes>;
    using _limited_str_ptr = _ptr<_limited_str_>;
    using _limited_str_ptr_container = _container<_limited_str_ptr, _maxStrIndexes>;

Full code:

#include <iostream>
#include <memory>
#include <array>

template<class T, size_t MaxIndex>
class BaseIndex
{
public:
    virtual ~BaseIndex() = default;

    explicit BaseIndex(const size_t index)
        : _maxIndex(MaxIndex), _currentIndex(index) {}

    virtual void DoSmth() = 0;

    friend std::ostream & operator << (std::ostream & ss, BaseIndex & base_index)
    {
        ss << "Max: " << base_index._maxIndex << ". Current: " << base_index._currentIndex << std::endl;
        return ss;
    }

protected:

    const size_t _maxIndex;

    size_t _currentIndex{ 0u };

};

template<size_t MaxIndex>
class IndexStr : public BaseIndex<std::string, MaxIndex>
{
public:
    explicit IndexStr(const size_t index) :BaseIndex(index) {}

    void DoSmth() override { std::cout << IndexStr::_maxIndex; }
};

template<size_t MaxIndex>
class IndexInt :public BaseIndex<int, MaxIndex>
{
public:
    explicit IndexInt(const size_t index) :BaseIndex(index) {}

    void DoSmth() override { std::cout << IndexInt::_maxIndex; }
};

class Manager
{
private:

    static const size_t _startStrIndex{ 11u };
    static const size_t _startIntIndex{ 10060u };

    static const size_t _maxStrIndexes{ 5 };
    static const size_t _maxIntIndexes{ 120 };

public:

    template <class T, size_t Size>
    using _container = const std::array<T, Size>;

    template <class T>
    using _ptr = std::shared_ptr<T>;

    // is it possible to combine this 2 types and use _maxStrIndexes only once?
    using _limited_str_ = IndexStr<_maxStrIndexes>;
    using _limited_str_ptr = _ptr<_limited_str_>;
    using _limited_str_ptr_container = _container<_limited_str_ptr, _maxStrIndexes>;

    // const std::array<std::shared_ptr<IndexStr<_maxStrIndexes>>, _maxStrIndexes> _strIndexes
    _limited_str_ptr_container _strIndexes
    {
        // _startStrIndex
        // create indexes ???
        // std::integer_sequence ???
        std::make_shared<_limited_str_>(_startStrIndex), // 11 = _startStrIndex
        std::make_shared<_limited_str_>(12),
        std::make_shared<_limited_str_>(13),
        std::make_shared<_limited_str_>(14),
        std::make_shared<_limited_str_>(_startStrIndex + _maxStrIndexes - 1)
    };

    // is it possible to combine this 2 types and use _maxIntIndexes only once?
    using _limited_int_ = IndexInt<_maxIntIndexes>;
    using _limited_int_ptr = _ptr<_limited_int_>;
    using _limited_int_ptr_container = _container<_limited_int_ptr, _maxIntIndexes>;

    _limited_int_ptr_container _intIndexes
    {
        // _startIntIndex
        // create indexes???
        std::make_shared<_limited_int_>(_startIntIndex), // 10060u  _startStrIndex
        //...
        std::make_shared<_limited_int_>(_startIntIndex + _maxIntIndexes -1)
    };
};

template <class T, size_t Size >
void Process(const std::array<std::shared_ptr<T>, Size> _array)
{
    for (auto &&baseindex : _array)
        std::cout << *baseindex;
}

int main()
{
    Manager m;

    Process(m._strIndexes);

    //Process(m._intIndexes);

    return 0;
}

Live demo


Solution

  • About the first question: yes, it is possible. It is even quite easy provided that you can use C++11 and variadic templates.

    To generate a compile-time list of indices, you can use std::make_index_sequence<N>, which will return a std::index_sequence<0, 1, 2, 3, ..., N-1>. You can then pattern-match it with the function creating the compile-time array:

    template <std::size_t... Ns>
    constexpr auto fill_it_at_compile_time_impl(std::index_sequence<Ns...>) {
        return std::array<unsigned, sizeof...(Ns)>{ Ns... };
    }
    
    template <std::size_t N>
    constexpr auto fill_it_at_compile_time() {
        return fill_it_at_compile_time_impl(std::make_index_sequence<N>());
    }
    

    If you want to add an offset to the index_sequence members, just do so in the array initialization:

    constexpr auto offset = 10u;
    template <std::size_t... Ns>
    constexpr auto fill_it_at_compile_time_impl(std::index_sequence<Ns...>) {
        return std::array<unsigned, sizeof...(Ns)>{ (offset+Ns)... };
    }
    

    About the second question: As far as I understand it, yes, it's possible. First, create a helper struct to query the index of _limited_str:

    template <typename T>
    struct query_max_index; 
    template <std::size_t N>
    struct query_max_index<IndexStr<N>> {
        static const auto max_index = N;
    };
    

    Then, rather than refering to _maxStrIndexes directly, you can query it from indirectly from _limited_str_ptr:

    using _limited_str_ = IndexStr<_maxStrIndexes>;
    using _limited_str_ptr = _ptr<_limited_str_>;
    using _limited_str_ptr_container = _container<_limited_str_ptr, query_max_index<std::decay_t<decltype(*std::declval<_limited_str_ptr>())>>::max_index>;