Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

initializing std::array with nullptr without template parameters

I am using an API that expects a contiguous block of memory that contains pointers. the pointers themselves can be nullptr.

Currently, I use C-Arrays:

ID3D11ShaderResourceView* srvs[] = {
    room_diffuse_srv.Get(),
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};
ctx->VSSetShaderResources(0, UINT(std::size(srvs)), std::data(srvs));

My toolchain contains clang-tidy, which advises to avoid C arrays and prefer std::array instead.

std::array has a deduction guide, that lets me initialize such an array like this:

std::array srvs = {
    room_diffuse_srv.Get(),
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};

This only works when the first element and all following elements are implicitly convertible to the same (pointer) type, in this case ID3D11ShaderResourceView*.

It breaks apart, as soon as the first element contains a nullptr:

std::array srvs = {
    nullptr,
    lightmap_srv.Get(), // error: "a value of type "ID3D11SamplerState *" cannot be used to initialize an entity of type "std::_Enforce_same<std::nullptr_t, std::nullptr_t, ID3D11SamplerState *, std::nullptr_t, std::nullptr_t, ID3D11SamplerState *>::type" (aka "std::nullptr_t")"
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
};

makes sense, because the first element has the type std::nullptr_t.

Can I initialize/deduce an std::array without specifying the type explicitly and allow the first element to be nullptr? I could cast the first (nullptr) element, but then I could just write it in the type as well.


2 Answers

I think you'll need to create a helper function that can deduce the type for you:

#include <array>
#include <type_traits>
#include <utility>

template<typename... T>
constexpr auto make_array(T&&... args)
    -> std::array<std::common_type_t<T...>, sizeof...(T)>
{
    return { std::forward<T>(args)..., };
}

Then we can write:

auto srvs = make_array(
    nullptr,
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
);
like image 152
Toby Speight Avatar answered Jun 26 '26 09:06

Toby Speight


You could use std::to_array:

auto srvs = std::to_array<ID3D11ShaderResourceView*>({
    nullptr,
    lightmap_srv.Get(),
    room_normal_srv.Get(),
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    input.ambient_occlusion,
    reflection_srv.Get(),
    reflection_extents_srv.Get(),
    nullptr,
    nullptr,
    nullptr
});
like image 38
Ted Lyngmo Avatar answered Jun 26 '26 09:06

Ted Lyngmo