1
int run(std::string type, std::string interval)
{
  if(type == "ST"){
    if(interval == "SEC"){
      constexpr unsigned int N = 10;
      Runner<N> data();
      data.parse();
    } else{
      constexpr unsigned int N = 20;
      Runner<N> data();
      data.parse();
    }
  }
  else if(type == "JST"){
    constexpr unsigned int N = 23;
    Runner<N> data();
    data.parse();
  }
  else{
    constexpr unsigned int N = 5;
    Runner<N> data();
    data.parse();
  }
}

I want to reduce the if statements and do the conditional checks on a separate function:

constexpr unsigned int arraysize(std::string type, std::string interval){
  if(type == "ST"){
    if(interval == "SEC"){
      return 10;
    } else{
      return 20;
    }
  }
  else if(type == "JST"){
    return 23;
  }
  else{
    return 5;
  }
}

However this doesn't work because a constexpr function cannot have a parameter of nonliteral type std::string.

Is there a better way to take out conditional checks so that I end up with something like this:

int run(std::string type, std::string interval)
{
  constexpr unsigned int N = arraysize(type, interval);
  Runner<N> data();
  data.parse();
}
4
  • 2
    I'm convinced that arraysize should work in C++20 (or, since not all compilers support them yet, change the parameters to std::string_view). But run() won't be able to call it like this, because function parameters are never considered to be constexpr inside of it. Commented Oct 21, 2022 at 7:12
  • [OT]: Runner<N> data(); would be a function declaration... -> Runner<N> data{}; Commented Oct 21, 2022 at 12:46
  • @JasonLiam: Off topic. Commented Oct 21, 2022 at 12:51
  • @Jarod42 Ah, I see. Commented Oct 21, 2022 at 12:52

2 Answers 2

2

run as you are showing can't work, even in principle, because you want N to be runtime-dependent. N can't be constexpr.

Alternative approach:

template<auto V>
inline constexpr auto constant = std::integral_constant<decltype(V), V>{};

template<typename F>
void apply_with_arraysize(F&& f, std::string type, std::string interval){
  if(type == "ST"){
    if(interval == "SEC"){
      f(constant<10>);
    } else{
      f(constant<20>);
    }
  }
  else if(type == "JST"){
    f(constant<23>);
  }
  else{
    f(constant<5>);
  }
}

int run(std::string type, std::string interval)
{
    apply_with_arraysize([](auto N){
        Runner<N()> data; // N instead of N() also ok, if `Runner` doesn't use `auto` non-type template argument
        data.parse();
    }, type, interval);
}
Sign up to request clarification or add additional context in comments.

4 Comments

wow the alternative approach contains lots of stuff that I'm not familiar with. If i had to guess, the constexpr auto constant allows N to be NOT runtime dependent. I would appreciate it if you could help me understand how std::integral_constant<decltype(V), V> makes it non-runtime dependent.
@MoneyBall constant is just a variable template that essentially makes constant<10> to be the same as std::integral_constant<int, 10>{} which is annoying to write. The point to this is that std::integral_constant encodes the value 10 in its type. Every instance of the class is stateless, the value we want to use is entirely contained in the type. And so the value can be used as a compile-time constant.
@MoneyBall The class exports the value again as static member value, but it also has a conversion operator to the stored type, so just N also works by implicit conversion (assuming the template doesn't use a auto non-type template argument, in which case fall back to N(). The class also has a operator() that returns the encoded value.) See en.cppreference.com/w/cpp/types/integral_constant, it is very easy to implement, no magic in there.
this has been so helpful! thank you so much!
1

Another alternative, with more overhead, is the std::variant approach:

std::variant<
    std::integral_constant<unsigned, 10>,
    std::integral_constant<unsigned, 20>,
    std::integral_constant<unsigned, 23>,
    std::integral_constant<unsigned, 5>,
>
arraysize(std::string type, std::string interval){
  if (type == "ST") {
    if (interval == "SEC") {
      return std::integral_constant<unsigned, 10>();
    } else {
      return std::integral_constant<unsigned, 20>();
    }
  } else if (type == "JST") {
    return std::integral_constant<unsigned, 23>();
  } else {
    return std::integral_constant<unsigned, 5>();
  }
}


int run(std::string type, std::string interval)
{
    std::visit(
        [](auto N){
            Runner<N> data{};
            data.parse();
        },
        arraysize(type, interval));
}

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.