Carneiro
Carneiro

Reputation: 3509

c++ creating templated objects programmatically

Suppose I want to instantiate many objects that come from a templated class (something like std::bitset) from bitset<1> to bitset<10>.

for (size_t i = 1; i <= 10; ++i) {
  std::bitset<i> my_bitset;
  // do stuff with it...
}

obviously this won't compile because i is not a literal or a constexpr.

Is there a way to do this? I'm thinking everything template metaprogramming possible in my head but I can't figure this one out. Any pointers appreciated.

Upvotes: 1

Views: 299

Answers (4)

rodrigo
rodrigo

Reputation: 98358

Something like (not tested):

template<int N>
struct InstantBS
{
    std::bitset<N> bs;
    InstantBS<N-1> next;
};

template<>
struct InstantBS<0>
{
};

template struct InstantBS<10>; //instantiate bitset<1> to bitset<10>

UPDATE: Well, I have tested it, and it does not work! The problem is that the members of InstantBS are not implicitly instantiated. And unfortunately, explicit instantiation must occur at namespace level, so you cannot force a explicit instantiation from another explicit instantiation. Unfortunately, template namespaces are not invented yet...

The closest think I can devise is this, doing manual instantation of any member of the bitset you need:

template<int N>
struct InstantBS
{
    void DoThings()
    {
        std::bitset<N> bs;
        bs.set();
        bs.reset();
        bs.flip();
        //any other operation you want to instantiate

        InstantBS<N-1> next;
        next.DoThings();
    }
};

template<>
struct InstantBS<0>
{
    void DoThings()
    {
    }
};
template struct InstantBS<10>; //instantiate bitset<1> to bitset<10>, more or less

You can check that the requestet members of the bitsets are actually instantiated:

$ g++ -c test.cpp
$ objdump -t test.o | c++filt | grep bitset

Upvotes: 2

Matthieu M.
Matthieu M.

Reputation: 299820

It is not possible, as you realized, to use a runtime variable as a template parameter; however should you know the list of values to use at compile-time, then you can indeed have a way to invoke tests for each element of this list.

template <template <size_t> class F>
void run() {}

template <template <size_t> class F, size_t H, size_t... Tail>
void run() { F<H>()(); run<F, Tail...>(); }

Then it is just a matter of defining F:

template <size_t N>
struct BitSetPlay {
    void operator()() {
        std::bitset<N> b;
        b.flip();
        std::cout << b.to_ulong() << "\n";
    }
};

Putting it altogether:

#include <bitset>
#include <iostream>

template <template <size_t> class F>
void run() {}

template <template <size_t> class F, size_t H, size_t... Tail>
void run() { F<H>()(); run<F, Tail...>(); }

template <size_t N>
struct BitSetPlay {
    void operator()() {
        std::bitset<N> b;
        b.flip();
        std::cout << b.to_ulong() << "\n";
    }
};

int main() {
    run<BitSetPlay, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u>();
    return 0;
}

Note: this assumed a possibly discontiguous list, if it is a range you wish for then you can do without variadic templates by simply keeping track of the bounds.

Upvotes: 3

Daniel E.
Daniel E.

Reputation: 206

see docs: The size of a bitset is fixed at compile-time (determined by its template parameter). For a class that also optimizes for space allocation and allows for dynamic resizing, see the bool specialization of vector (vector).

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409176

It's not possible, because templates are a compile-time only concept. You can't use runtime data to declare templated instances.

Template arguments have to be types, or compile-time constants.

Upvotes: 2

Related Questions