简体   繁体   中英

Using `std::accumulate` with constexpr

Original

Given a std::array<std::variant<>> I get the total byte size (eg {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

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.

Original

Given a std::array<std::variant<>> I get the total byte size (eg {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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM