6
\$\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 Answers 2

    4
    \$\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\$
    2
    • \$\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\$CommentedOct 24, 2018 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
      CommentedOct 24, 2018 at 11:58
    3
    \$\begingroup\$

    With C++20's consteval this is now possible without static_assert or macro's.
    Solution based on C++20 to eliminate runtime bugs and Compile-time format string checks.

    #include <algorithm> #include <array> #include <string_view> using namespace std::string_view_literals; constexpr auto COEFFICIENTS = std::array{ "VERSION"sv, "CHANNELS"sv, "POWER"sv }; struct CoefficientName { std::string_view str; consteval CoefficientName(std::string_view name) :str(name) { if(std::ranges::find(COEFFICIENTS, name) == COEFFICIENTS.end()) throw; } }; void modifyCoefficient(CoefficientName name, int value) { } int main() { modifyCoefficient("CHANNELS"sv, 42); modifyCoefficient("CURRENT"sv, 99); // compilation error modifyCoefficient("POWER"sv, 1); return 0; } 

    https://godbolt.org/z/M3oE4acxc

    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.