1

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.

6
  • 1
    Make your own bit vector class, it would be relatively simple to do as a wrapper around std::vector<bool> Commented Nov 20, 2013 at 7:54
  • 3
    I think you're looking for dynamic_bitset. Commented Nov 20, 2013 at 7:54
  • 1
    What's your purpose in forcing the instantiations? What functionality are you trying to get? Commented Nov 20, 2013 at 7:56
  • I am not using bitset (this was just to simplify the example) Commented Nov 20, 2013 at 7:58
  • 1
    A template is not a class at all, only its specialisations are classes. Any failures arising out of the result of using a template class will do so at compile time. Can you not just test a handful of specialisations? If the logic varies between specialisations, perhaps this is a situation which doesn't warrant a template class? Commented Nov 20, 2013 at 8:13

4 Answers 4

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

yes it is the case that you know all the numbers to be tested at compile time, this looks interesting. I will give it a shot.
2

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.

3 Comments

@Carneiro Yes, template-metaprogramming is just way of using (or abusing some would say) the compiler to generate code for you, but it's still all done during compilation.
@Carneiro Best template metaprogramming program ever written was the first. It produced the prime number series in the compiler error messages. Proof that template metaprogramming happens at compile time.
this is very misleading - you definately can do this with recursive templates instead of using a for loop
2

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

2 Comments

Beat me to it - only thing I was doing was abstracting out bitset and using template<typename T, int N> instead - so it can be re-used for all the classes he had. Was also considering taking another template arg a driver as a functor that took T as an arg. -- so +1 for saving me the trouble of writing all that :)
@GlennTeitelbaum: I'm afraid it does not work as expected, because members of the explicit instantiated class are not explicitly instantiated. See my updated answer...
0

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.