1

I have the following code:

static const char* ITEMS[] = { "a", "b" }  // too many elements for a std::array in my situation

void my_func() {
    printf("a: %d", index_of(ITEMS, "a"));  // 0
    printf("d: %d", index_of(ITEMS, "d"));  // error at compile time; "d" does not exist in ITEMS.
}

How would I define a similar index_of method? I've seen various suggestions on other SO posts, but they don't work for the following reasons:

  • Use std::string/Make ITEMS a constexpr
    • Unfortunately ImGui's Combo requires a const char** for the array type which is impossible with std::string, and similarly, as ITEMS would be a constexpr it would be of type const char* const *, which does not work.
  • Use std::array instead of T[]
    • The number of elements is too high for the compiler to handle, resulting in a compiler error.
6
  • "too many elements for a std::array in my situation" - std::array<T,N> is just a wrapper for a T[N] array, so they should have the same compiler limitations. "The number of elements is too high for the compiler to handle, resulting in a compiler error" - that would imply that your design is wrong to begin with and should be rewritten. Commented Jan 20, 2023 at 1:53
  • It can only possibly work at compile-time if it is guaranteed that the pointers themselves are const. If the pointers may be modified by a function, then they are not compile-time constants. Commented Jan 20, 2023 at 1:56
  • I can see some nuisance with the length of the array not being inferred, but other than that it's as Remy says: Same size. Commented Jan 20, 2023 at 1:56
  • > that would imply that your design is wrong to begin with and should be rewritten using const char* ITEMS[] = { ... } works fine, using auto ITEMS[] = std::array { ... } yields In template: instantiating fold expression with 1821 arguments exceeded expression nesting limit of 256 > It can only possibly work at compile-time if it is guaranteed that the pointers themselves are const How would I specify this in code? None of them are modified at runtime. Commented Jan 20, 2023 at 1:58
  • @Martmists You can increase the instantiation limit with a compiler option and it occurs only because you are relying on CTAD to determined the array size instead of specifying it explicitly. (Although 256 does look like a small default limit to me as well.) Commented Jan 20, 2023 at 2:01

1 Answer 1

3

First, you must declare the array as constexpr. Otherwise its values are not usable at compile-time:

static constexpr const char* ITEMS[] = { "a", "b" };

As a consequence the type of the elements will be const char* const. If a library function expects them to be non-const then presumably that is because the library may attempt to modify them. That is of course impossible if they are supposed to be compile-time constants.

If you are absolutely sure that the library is simply lying about needing to modify the pointer value, then you can cast const away with const_cast. However, if the library then does attempt to modify the elements, then your program will have undefined behavior.

Then you also need C++20. Otherwise there is no way to force a compile-time error from failure of constant expression evaluation in a simple function call. Specifically you need the consteval feature.

With that:

template<typename R>
consteval auto index_of(const R& range, std::string_view needle) {
    auto it = std::ranges::find(range, needle);
    if(it == std::ranges::end(range))
        throw std::logic_error("Element not found!");
    return std::ranges::distance(std::ranges::begin(range), it);
}

This works with both built-in arrays and std::array.

Then you also need to replace %d with %zu, because the iterator difference type returned by this index_of is std::size_t for built-in arrays. %d is for the wrong type (int).

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

2 Comments

Is there a way to also define a non consteval overload as a fall back? I.e. the support both index_of(ITEMS, "a") and index_of(ITEMS, myObj.variable)? How does this work with overload resolutions
@bgura No, unfortunately that is currently impossible. You can't overload on consteval or whether the call would be a constant expression in general. It would however be possible if you are fine with implementing index_of as a macro. The standard library uses the trick for std::format to do compile-time format string checks. They chose to use a different function name std::vformat for the runtime form that doesn't perform these checks and can be called with runtime arguments.

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.