6

given following templates and specialization

enum CountryName 
{
    Armenia = 0 ,
    Georgia,
    Size = 2
};

template <CountryName variable>
class CountryInfo;

template <>
class CountryInfo<Armenia> 
{
    /* CODE HERE */
};

template <>
class CountryInfo<Georgia> 
{
    /* CODE HERE */
};

I would like to iterate over enum and create object for each specialization.

main() {
    for(auto i=0; i<CountryName::Size; ++i) {
        CountryInfo<(static_cast<CountryName>(i))>();
    }       
}   

I get following error: error: the value of 'i' is not usable in a constant expression CountryInfo<(static_cast(i))>();

libxelar.so
  • 473
  • 4
  • 16

4 Answers4

2

As I said in the comments, templates are resolved at compile-time. I.e. only constant values can be used as template parameters, which the variable i is not.

What you can do is some kind of recursive template iteration:

template<CountryName c>
struct ForLoop {
    template<template <CountryName> class Func>
    static void iterate() {
        ForLoop<static_cast<CountryName>(c - 1)>::template iterate<Func>();
        Func<c>()();
    }
};

//so that compiler knows when to stop
template <>
struct ForLoop<Armenia> {
  template <template <CountryName> class Func>
  static void iterate() {
    Func<Armenia>()();
  }
};

// CountryInfo needs an overloaded ()-operator, whcih get's called in the ForLoop
template <CountryName n>
struct CountryInfo {
  void operator()() { std::cout << n << std::endl; }
};

int main() {
  ForLoop<Georgia>::iterate<CountryInfo>();
  return 0;
}

In the main-function the static ForLoop<Georgia>::iterate-function get's called, this function then substracts 1 from Georgia and calls the function iterate again, until it hits the ForLoop<Armenia>::iterate which is the last function which get's called. In case you have any questions, let me know.

Mike van Dyke
  • 2,724
  • 3
  • 16
  • 31
2

What you want is convert a run-time variable into a compile-time variable (which is the requirement for a template argument). There are various ways to achieve this, for example

enum struct Country {
    Armenia, Georgia, India
};

template<template<County> class Functor, typename... Args>
void LoopCountries(Args&&...args)
{
    { Functor<Armenia> func; func(std::forward<Args>(args)...); }
    { Functor<Georgia> func; func(std::forward<Args>(args)...); }
    { Functor<India> func; func(std::forward<Args>(args)...); }
}

which assumes that Functor<> has a member operator(). Now you can simply

LoopCountries<CountryInfo>();

A more common situation is to pick one value (instead of looping over all):

template<template<County> class Functor, typename... Args>
void SwitchCountry(Country country, Args&&...args)
{
    switch(country) {
    case Armenia: { Functor<Armenia> func; func(std::forward<Args>(args)...); }
    case Georgia: { Functor<Georgia> func; func(std::forward<Args>(args)...); }
    case India: { Functor<India> func; func(std::forward<Args>(args)...); }
    }
}
Walter
  • 44,150
  • 20
  • 113
  • 196
2

As explained by Mike van Dike, a template parameter needs to be known compile-time but your i is modified run time.

You have to use compile-time known indexes.

If you can use C++14, you can use variadic templates, std::make_index_sequence and std::index_sequence so you can do something as follows (see iterateCountry())

#include <tuple>
#include <type_traits>

enum CountryName 
{
    Armenia = 0 ,
    Georgia,
    Size = 2
};

template <CountryName variable>
class CountryInfo;

template <>
class CountryInfo<Armenia> 
{
    /* CODE HERE */
};

template <>
class CountryInfo<Georgia> 
{
    /* CODE HERE */
};

template <std::size_t ... Is>
auto iterateCountry (std::index_sequence<Is...> const &)
 { return std::make_tuple(CountryInfo<static_cast<CountryName>(Is)>{}...); }


int main ()
 {
   auto ict { iterateCountry(
                 std::make_index_sequence<static_cast<std::size_t>(
                    CountryName::Size)>{}) };

   static_assert(std::is_same<decltype(ict),
                              std::tuple<CountryInfo<Armenia>,
                                         CountryInfo<Georgia>>>{}, "!");
 }

-- EDIT --

The OP ask

I was seeking for way to somehow iterate over countries and create objects. link line 5344.

It seem's to me that my solution make exactly this.

For your line 5344 case, I suppose you should apply my solution adding a delegating constructor; something as

template <std::size_t ... Is>
CountryInfoManager (std::index_sequence<Is...> const &)
  : m_countries{ new CountryInfo<static_cast<CountryName>(Is)>{}... }
 { }

CountryInfoManager ()
 : CountryInfoManager(
      std::make_index_sequence<static_cast<std::size_t>(
                CountryName::Size)>{})
 { }
max66
  • 65,235
  • 10
  • 71
  • 111
  • This is close to what I am looking for, but your solution creates tuple with special type.What I would like to have an iteration which creates objects of all specalization types. – libxelar.so Aug 14 '18 at 15:22
  • @libxelar.so - sorry but I don't understand what do you mean. My solution create a `std::tuple, CountryInfo>` (see the final `static_assert()`). What's your target? – max66 Aug 14 '18 at 16:43
  • consider following example, I have a lot specializations by value , like CountryInfo , CountryInfo etc, where values are from CountryName enum. I would like automate following statement using some compiletime trick ` new CountryInfo; new CountryInfo; new CountryInfo; .. 1000 similar expressions here ... new CountryInfo;` – libxelar.so Aug 14 '18 at 20:24
  • @libxelar.so - so do you want a `std::tuple` of dynamic allocated pointers to all different `CountryInfo` types instead of a `std::tuple` of objects? – max66 Aug 14 '18 at 21:34
  • I was seeking for way to somehow iterate over countries and create objects. [link](https://github.com/mm-project/qt_sms_partying/blob/master/src/sms/country.hpp) line 5344. It was just out of curiosity, off course parsing like that should be completed in run-time. but in any case. – libxelar.so Aug 15 '18 at 12:45
  • 1
    @libxelar.so - answer modified; hope this helps – max66 Aug 15 '18 at 13:25
1

You could use something like this:

template<std::size_t... I>
constexpr auto
countries(std::index_sequence<I...>)
{
    return std::make_tuple(CountryInfo<static_cast<CountryName>(I)>{}...);
}

constexpr auto
all_countries()
{
    return countries(std::make_index_sequence<Size>());
}

The result will be a tuple, with each index containing a country of corresponding type.

eerorika
  • 232,697
  • 12
  • 197
  • 326