2
\$\begingroup\$

We have a modifyCoefficient(const char* name, int value) function that updates the value of a coefficient in a container. The names are known at compile time, they are read from an XML file in a pre-build step and stored in an array.
Usage: data.modifyCoefficient("ADAPTIVE", 1);
Compiler: MSVC 2017 v15.8

I would like to get a compile time error when the coefficient name does not exist.
With the following code that happens, but is there a way to do it without a macro?

#include <array>

#define COEFF(name) returnName<coefficientExists(name)>(name)

constexpr std::array<const char*, 3> COEFFICIENTS = { "VERSION", "CHANNELS", "ADAPTIVE" };

constexpr bool coefficientExists(const char* name)
{
    for (auto coefficientIndex = 0U; coefficientIndex < COEFFICIENTS.size(); ++coefficientIndex)
    {
        if (COEFFICIENTS[coefficientIndex] == name)
            return true;
    }
    return false;
}

template<bool CoefficientTest>
constexpr const char* returnName(const char* name)
{
    static_assert(CoefficientTest, "coefficient does not exist");
    return name;
}

int main()
{
    static_assert(coefficientExists("VERSION"), "should exist");
    static_assert(coefficientExists("TEST") == false, "should not exist");
    static_assert(COEFF("ADAPTIVE") == "ADAPTIVE", "should return name");
    COEFF("CHANNELS");
    // data.modifyCoefficient(COEFF("ADAPTIVE"), 1);
    return 0;
}

https://godbolt.org/z/kpGcMS

\$\endgroup\$
2
\$\begingroup\$

You have to compare values char by char, or even simpler change const char* to std::string_view make your code work.

Thanks to the deduction guides, you can just write std::array = ... and template parameters will be automatically deduced.

Also, you can simplify your loop using a "range-based for". If we generalize for other types of compile-time array, we get:

#include <array>
#include <string_view>

using namespace std::string_view_literals;
constexpr std::array COEFFICIENTS = { "VERSION"sv, "CHANNELS"sv, "ADAPTIVE"sv };

template <typename T, size_t N>
constexpr bool array_has(const std::array<T, N>& array, const T& value) {
  for (const auto& v : array) {
    if (value == v) return true;
  }
  return false;
}

constexpr bool coefficientExists(const std::string_view name)
{
    return array_has(COEFFICIENTS, name);
}

int main()
{
    static_assert(coefficientExists("VERSION"), "should exist");
    static_assert(!coefficientExists("TEST"), "should not exist");
}

With C++20 you can even do simpler:

#include <algorithm>
//...
template <typename T, size_t N>
constexpr bool array_has(const std::array<T, N> array, const T value) {
return std::any_of(array.begin(), array.end(), [value](auto current){ return current == value; });
} 
\$\endgroup\$
  • \$\begingroup\$ Should array_has take its arguments by const reference, rather than const value? I'd be inclined to make v a const ref, too. You could argue that it's unnecessary, but it could help performance when called with non-constexpr arguments. \$\endgroup\$ – Toby Speight Oct 24 '18 at 11:46
  • \$\begingroup\$ Constexpr range-based for with std::array and std::string_view don't seem to be supported by MSVC 2017 v15.8. The problem with coefficientExists("VERSION") is that it is not automatically evaluated at compile time. It's only being evaluated at compile time because of the static_assert, that's why I had to use the macro. \$\endgroup\$ – Synck Oct 24 '18 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.