Skip to content

Instantly share code, notes, and snippets.

@lichray
Last active August 29, 2023 16:37
Show Gist options
  • Save lichray/6034753 to your computer and use it in GitHub Desktop.
Save lichray/6034753 to your computer and use it in GitHub Desktop.
Factory function of std::array
#include <array>
#include <functional>
template <typename... T>
using common_type_t = typename std::common_type<T...>::type;
template <typename T>
using remove_cv_t = typename std::remove_cv<T>::type;
template <bool, typename T, typename... U>
struct lazy_conditional_c;
template <typename T>
struct lazy_conditional_c<true, T>
{
using type = typename T::type;
};
template <typename T, typename U>
struct lazy_conditional_c<true, T, U>
{
using type = typename T::type;
};
template <typename T, typename U>
struct lazy_conditional_c<false, T, U>
{
using type = typename U::type;
};
template <typename V, typename T, typename... U>
using if_else = lazy_conditional_c<V::value, T, U...>;
template <typename V, typename T, typename... U>
using If = typename if_else<V, T, U...>::type;
template <typename T>
struct identity_of
{
using type = T;
};
template <template <typename> class F, typename... T>
struct no_type : std::true_type {};
template <template <typename> class F, typename T1, typename... T2>
struct no_type<F, T1, T2...> :
std::integral_constant
<
bool,
not F<T1>::value and no_type<F, T2...>::value
>
{};
template <template <typename> class F, template <typename> class G>
struct composed
{
template <typename T>
using call = F<typename G<T>::type>;
};
template <typename T>
struct _is_reference_wrapper : std::false_type {};
template <typename T>
struct _is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T>
using is_reference_wrapper =
composed<_is_reference_wrapper, std::remove_cv>::call<T>;
template <typename V = void, typename... T>
constexpr auto make_array(T&&... t)
-> std::array
<
If
<
std::is_void<V>,
std::common_type<T...>,
identity_of<V>
>,
sizeof...(T)
>
{
static_assert(not std::is_void<V>() or
no_type
<
composed
<
is_reference_wrapper,
std::decay
>
::call,
T...
>(), "T shall not be reference_wrapper");
return {{ std::forward<T>(t)... }};
}
template <size_t... I>
struct _indices {};
template <size_t N, size_t... I>
struct _build_indices : _build_indices<N - 1, N - 1, I...> {};
template <size_t... I>
struct _build_indices<0, I...> : _indices<I...> {};
template <typename T, size_t N, size_t... I>
constexpr auto _to_array(T (&arr)[N], _indices<I...>)
-> std::array<remove_cv_t<T>, N>
{
return {{ arr[I]... }};
}
template <typename T, size_t N, size_t... I>
constexpr auto _to_array(T (&&arr)[N], _indices<I...>)
-> std::array<remove_cv_t<T>, N>
{
return {{ std::move(arr[I])... }};
}
template <typename T, size_t N>
constexpr auto to_array(T (&arr)[N])
-> std::array<remove_cv_t<T>, N>
{
return _to_array(arr, _build_indices<N>());
}
template <typename T, size_t N>
constexpr auto to_array(T (&&arr)[N])
-> std::array<remove_cv_t<T>, N>
{
return _to_array(std::move(arr), _build_indices<N>());
}
#include <iostream>
#include <memory>
int main()
{
auto ch = 'a';
auto const d = 65l;
auto a1 = make_array(ch, d, 0);
static_assert(std::is_same<decltype(a1)::value_type, long>(), "");
std::cout << a1[0] << std::endl;
constexpr auto a2 = to_array("abc");
static_assert(std::is_same<decltype(a2)::value_type, char>(), "");
std::cout << a2.data() << std::endl;
auto a3 = make_array("aa", "bb");
static_assert(std::is_same<decltype(a3)::value_type, char const*>(),
"fix your library");
std::cout << a3[0] << std::endl;
auto a4 = make_array<long>(2, 3U);
static_assert(std::is_same<decltype(a4)::value_type, long>(), "");
std::cout << a4[0] << std::endl;
auto a5 = make_array<short>();
static_assert(std::is_same<decltype(a5)::value_type, short>(), "");
std::cout << a5.size() << std::endl;
int a, b;
auto a6 = make_array<std::reference_wrapper<int>>(std::ref(a), b);
std::cout << a6.size() << std::endl;
auto a7 = to_array<std::pair<int, float>>(
{ { 3, .0f }, { 4, .1f }, { 4, .1e23f } });
static_assert(
std::is_same<decltype(a7)::value_type, std::pair<int, float>>(),
"");
auto a8 = to_array({ std::make_unique<int>(3) });
std::cout << *a8[0] << std::endl;
auto a9 = to_array({ 0, 2, 1, 3 });
std::cout << a9.size() << std::endl;
// ** do not compile **
//auto a7 = make_array(std::cref(""));
// ** hard error **
//char s[2][6] = { "nice", "thing" };
//auto a8 = to_array(s);
}
@boatilus
Copy link

Nice work! What's the current license?

@FrankHB
Copy link

FrankHB commented Dec 26, 2014

Is these interfaces stable now? I have similar ones for years...

@lichray
Copy link
Author

lichray commented Feb 9, 2015

The stable one is https://gist.github.com/lichray/6034753/337240ea9777c5118ba3430c5198c2d0d4f81a03 ; the "current" one is left here for records.

@dgodfrey206
Copy link

I haven't studied the code yet but what does all this do differently than http://stackoverflow.com/a/6114299/701092?

@lichray
Copy link
Author

lichray commented Mar 24, 2015

In the stackoverflow answer, all elements are required to be implicitly convertible to the first element:

make_array(1, 1L) -> std::array<int, 2>

In this gist, common type is calculated:

make_array(1, 1L) -> std::array<long, 2>

Alternatively, you can specify the target type:

make_array<long long>(1, 1L) -> std::array<long long, 2>

Although such a syntax works in the stackoverflow version, the first argument will no longer use perfect forwarding, because it abandons the deduction behavior.

@quietdragon
Copy link

What is the license for this gist ?

@Robbepop
Copy link

I need this very badly in the standard! This is an extremely useful function to me! Thanks for this gist and the proposal!

@nestal
Copy link

nestal commented Nov 25, 2015

I actually get there from a link in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4391.html
so it looks like it will be in a TS or C++17

@bklarson
Copy link

I can't get this to work with VS 2015, curious if anyone else has had luck?

struct Point { int x, y; };
auto triangle = std::make_array<Point>({3, 3}, {5, 5}, {1, 5});

c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1292): error C2440: 'return': cannot convert from 'Point [3]' to 'Point (&&)[3]'
c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1292): note: You cannot bind an lvalue to an rvalue reference
make_array.cc(125): note: see reference to function template instantiation 'Point (&&std::move<T(&)[3]>(_Ty) noexcept)[3]' being compiled 

It works fine with GCC 6.

@aCuria
Copy link

aCuria commented Apr 8, 2019

I love this, but am wondering if it can be improved. Would the syntax in #1 and #2 be possible?

// #1 How I would expect std::array to work
std::array<VkDescriptorPoolSize> poolSizes = (
	  VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
	, VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);

// #2 How I would expect make_array to work
auto poolSizes = make_array<VkDescriptorPoolSize>(
	  { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
	, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);

// #3 How make_array actually works
auto poolSizes = make_array(
	  VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
	, VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);

// #4 How C/C++ works without std::array
VkDescriptorPoolSize poolSizes[] = { 
	  {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount}
	, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}
};

edit: I realized #2 is -supposed- to work, but it does not for me (VS2017)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment