1

Original

Given a std::array<std::variant<>> I get the total byte size (e.g. {float,float,uint32_t}=4+4+4=12) and then I want to get the array in the form of a void* (to pass to Vulkan).

I don't believe I need heap allocation for this, although I'm stuck on how to pass the calculated size as a template parameter.

The Repl: https://repl.it/@JonathanWoollet/HumongousDistinctDefinitions

The error:

> clang++-7 -pthread -std=c++17 -o main main.cpp
main.cpp:42:30: error: constexpr variable 'size' must be initialized by a
      constant expression
    constexpr uint32_t const size = arrSize<num>(arr);
                             ^      ~~~~~~~~~~~~~~~~~
main.cpp:17:34: note: non-constexpr function 'accumulate<const
      std::variant<unsigned int, float> *, unsigned long, (lambda at
      main.cpp:19:9)>' cannot be used in a constant expression
    return static_cast<uint32_t>(std::accumulate(arr.cbegin(),arr.cend(),
                                 ^
main.cpp:42:37: note: in call to 'arrSize(arr)'
    constexpr uint32_t const size = arrSize<num>(arr);
                                    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_numeric.h:146:5: note: 
      declared here
    accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
    ^
main.cpp:44:21: error: no matching function for call to 'toVoidPtr'
    void* voidArr = toVoidPtr<num,size>(arr);
                    ^~~~~~~~~~~~~~~~~~~
main.cpp:25:7: note: candidate template ignored: invalid explicitly-specified
      argument for template parameter 'Size'
void* toVoidPtr(std::array<std::variant<uint32_t,float>,Num> const& arr) {
      ^
2 errors generated.
compiler exit status 1

(Compiling with C++20 leads to the same errors)

I've seen Compile-time equivalent to std::accumulate() but https://en.cppreference.com/w/cpp/algorithm/accumulate clearly shows a constexpr definition:

template< class InputIt, class T, class BinaryOperation >
constexpr T accumulate( InputIt first, InputIt last, T init,
                        BinaryOperation op );

I get the size by:

template <uint32_t Num>
constexpr uint32_t arrSize(std::array<std::variant<uint32_t,float>,Num> const& arr) {
    auto var_size = [](auto const& var) -> size_t {
        using T = std::decay_t<decltype(var)>;
        return sizeof(T);
    };
    return static_cast<uint32_t>(std::accumulate(arr.cbegin(),arr.cend(),
        std::size_t{ 0 },
        [var_size](std::size_t acc, auto const var) { return acc + std::visit(var_size,var); }
    ));
}

I convert to a void* by:

template <uint32_t Num, uint32_t Size>
void* toVoidPtr(std::array<std::variant<uint32_t,float>,Num> const& arr) {
    size_t byteCounter = size_t{0};
    std::array<std::byte,Size> bytes;
    std::for_each(arr.cbegin(),arr.cend(), [&](auto const& var) {
        std::visit([&] (auto const& var) {
            using T = std::decay_t<decltype(var)>;
            std::memcpy(bytes.data()+byteCounter,static_cast<void const*>(&var),sizeof(T));
            byteCounter += sizeof(T);
        },var);
    });
    return static_cast<void*>(bytes.data);
}

I want to follow an order like:

int main() {
    std::cout << "Hello World!\n";

    const uint32_t num = 3U;
    std::array<std::variant<uint32_t,float>,num> arr = { 1.34F, 3.76F, 124U };
    constexpr uint32_t const size = arrSize<num>(arr);
    std::cout << "size: " << size << std::endl;
    void* voidArr = toVoidPtr<num,size>(arr);
}

I'm not sure what I'm missing here, and I can't seem to figure it out. I would really appreciate any help.

(I'm missing anything please drop a comment and I'll try to add it)


With gcc 10.2.0 & c++20

After updating to use compatible builds tools as suggested by Werner Henze, and updating my local project to use gcc 10.2.0 Henze and c++20 I get different errors.

[build] In file included from C:\Users\jonat\Projects\gpu_blas\c++\test\ExampleTests.cpp:2:
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp: In instantiation of 'ComputeApp<NumPushConstants>::ComputeApp(const char*, uint32_t, const uint32_t*, float**, std::array<std::variant<unsigned int, float>, NumPushConstants>, std::array<unsigned int, 3>, std::array<unsigned int, 3>) [with unsigned int NumPushConstants = 1; uint32_t = unsigned int]':
[build] C:\Users\jonat\Projects\gpu_blas\c++\test\ExampleTests.cpp:41:5:   required from here
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:367:95:   in 'constexpr' expansion of 'Utility::pushConstantsSize<1>(pushConstant)'
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:102:53:   in 'constexpr' expansion of 'std::accumulate<const std::variant<unsigned int, float>*, long long unsigned int, Utility::pushConstantsSize<1>::<lambda(std::size_t, auto:43)> >((& pushConstants)->std::array<std::variant<unsigned int, float>, 1>::cbegin(), (& pushConstants)->std::array<std::variant<unsigned int, float>, 1>::cend(), 0, <lambda closure object>Utility::pushConstantsSize<1>::<lambda(std::size_t, auto:43)>{<lambda closure object>Utility::pushConstantsSize<1>::<lambda(const auto:42&)>()})'
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:367:38: error: 'pushConstant' is not a constant expression
[build]   367 |             constexpr uint32_t const sizeTester = Utility::pushConstantsSize<NumPushConstants>(pushConstant);
[build]       |                                      ^~~~~~~~~~
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp: In instantiation of 'ComputeApp<NumPushConstants>::ComputeApp(const char*, uint32_t, const uint32_t*, float**, std::array<std::variant<unsigned int, float>, NumPushConstants>, std::array<unsigned int, 3>, std::array<unsigned int, 3>) [with unsigned int NumPushConstants = 3; uint32_t = unsigned int]':
[build] C:\Users\jonat\Projects\gpu_blas\c++\test\ExampleTests.cpp:849:5:   required from here
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:367:95:   in 'constexpr' expansion of 'Utility::pushConstantsSize<3>(pushConstant)'
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:102:53:   in 'constexpr' expansion of 'std::accumulate<const std::variant<unsigned int, float>*, long long unsigned int, Utility::pushConstantsSize<3>::<lambda(std::size_t, auto:43)> >((& pushConstants)->std::array<std::variant<unsigned int, float>, 3>::cbegin(), (& pushConstants)->std::array<std::variant<unsigned int, float>, 3>::cend(), 0, <lambda closure object>Utility::pushConstantsSize<3>::<lambda(std::size_t, auto:43)>{<lambda closure object>Utility::pushConstantsSize<3>::<lambda(const auto:42&)>()})'
[build] c:\users\jonat\projects\gpu_blas\c++\example.hpp:367:38: error: 'pushConstant' is not a constant expression
[build] mingw32-make[2]: *** [test\CMakeFiles\ExampleTests.dir\build.make:82: test/CMakeFiles/ExampleTests.dir/ExampleTests.cpp.obj] Error 1
[build] mingw32-make[1]: *** [CMakeFiles\Makefile2:315: test/CMakeFiles/ExampleTests.dir/all] Error 2
[build] mingw32-make: *** [makefile:159: all] Error 2
[build] Build finished with exit code 2

I've done a little more playing around here but I can't quite find the problem, once again I would really appreciate any help.

Since I cannot give a Repl to reproduce this minimally, here is a link to the CMake project with the current branch producing these errors: https://github.com/JonathanWoollett-Light/gpu_blas/tree/Compiler-update/c%2B%2B

Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58
  • https://en.cppreference.com/w/cpp/algorithm/accumulate clearly notes that `accumulate` is only `constexpr` since C++20, not in C++17. – Werner Henze Feb 20 '21 at 12:35
  • @WernerHenze Compiling with C++20 `clang++-7 -pthread -std=c+2a -o main main.cpp` leads to the same errors. – Jonathan Woollett-light Feb 20 '21 at 13:12
  • From https://en.cppreference.com/w/cpp/compiler_support "constexpr for numeric algorithms P1645R1": requires clang libc++ 12. – Werner Henze Feb 20 '21 at 13:16
  • Just grappling with a bunch of new issues with my local cmake project after having updated the compiler, if you want to make this the answer, once I've been able to get it working I'll mark it as such (might take me awhile to get it working though `ninja: error` going to be a pain) – Jonathan Woollett-light Feb 20 '21 at 15:42

1 Answers1

1

cppreference accumulate says that accumulate is only constexpr since C++20, not in C++17. So you need to switch to C++20.

If you want to know which compiler already supports constexpr accumulate, you can take a look at cppreference compiler support and search for "constexpr for numeric algorithms"/P1645R1. For example for clang it requires libc++ 12.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
  • I updated to use `c++20` and a compatible compiler as you suggested (for my local project this being `gcc 10.2`), I get different errors, which is at least progress Thankyou so far. I've added an additional section to my question covering these new errors. – Jonathan Woollett-light Feb 21 '21 at 09:02
  • @JonathanWoollett-light In my opinion your original question was answered. Your new question is a different question and should be posted as a different question. Regarding your new error message: How should the compiler calculate `constexpr uint32_t const sizeTester = Utility::pushConstantsSize(pushConstant);` at compile time when `pushConstant` is only `const` and not `constexpr` and thus not known at compile time? – Werner Henze Feb 21 '21 at 09:44