c++arraysvisual-studio-2022constexprstatic-assert

C++ constexpr operations on each element of a constexpr array


Would be possible to iterate over a constexpr C-array/std::array/vector and execute constexpr operations on each element and everything to be done at compile time?

https://godbolt.org/z/5a4v3Eerh

#include <string_view>
using namespace std::string_view_literals;

constexpr std::string_view arr[] = {"ab"sv, "xyz"sv,};

int main(){
    static_assert(arr[0] == "ab");  //OK
    static_assert(arr[1] == "xyz");

    #if 0 //would something like this be possible?
    for(const auto i: arr){
        static_assert(i == "xyz"); //???
    }
    #endif
    return 0;
}

I used static_assert() but there could be any constexpr function ...

Any suggestion would be appreciated! any C++ standard (preferably the latest), any compiler (preferably Microsoft VisualStudio 2022)


Solution

  • After reading all your suggestions, my conclusions are:

    1. the simplest solution would be to use std::array<std:string_view> because it is constexpr-friendly and the operator==() can be used to compare 2 objects of this type:
    constexpr std::array<std::string_view,2> inp = {/*...*/};
    constexpr std::array<std::string_view,2> out = someConstexprFunc(inp);
    constexpr std::array<std::string_view,2> ref = {/*...*/};
    
    static_assert(out == ref);
    

    It is simpler but doesn't provide information about the index causing an eventual mismatch

    1. if the index causing the mismatch is needed then replace operator==() with a constexpr function returning the index and use that index as template parameter for an error class with a meaningful name:
    #include <algorithm>
    #include <array>
    #include <ranges>
    #include <string_view>
    using namespace std::string_view_literals;
    
    consteval auto run(std::ranges::range auto const& range){
        std::remove_cvref_t<decltype(range)> res;
        std::transform(range.cbegin(), range.cend(), res.begin(), [](const std::string_view s){
            return s.substr(0, 1);
        });
        return res;
    }
    
    template<size_t N>
    constexpr size_t cmp(const std::array<std::string_view,N>&a, const std::array<std::string_view,N>&b){
        for(size_t i=0; i<N; ++i){
            if(a[i] != b[i]){
                return i+1;
            }
        }
        return 0;
    }
    
    template<size_t index> struct CompareErrorAtIndex{
        static_assert(index == 0, "compare `out` with `ref`");
    };
    
    constexpr std::array<std::string_view,2> inp = {"ab"sv, "xyz"sv};
    constexpr std::array<std::string_view,2> out = run(inp);
    constexpr std::array<std::string_view,2> ref = {"a"sv, "x"sv,};
    
    int main(){
        //static_assert(out == ref); //OK but no info about index generating the error
    
        constexpr auto index = cmp(out, ref);
        if constexpr(index){
            CompareErrorAtIndex<index>();
        }
    
        return index;
    }
    

    The error class together with its template argument (the index) will appear in the build output for all 3 main compilers (msvc, clang, gcc) like, for example: ... CompareErrorAtIndex<1> ...

    You can see it in action at https://godbolt.org/z/z41TqhvPE

    Thank you very much for your input! (I'm still open for comments, suggestions, improvements, constructive critics)