I can write this and it works perfectly fine:
struct Foo
{
int i;
std::string s;
};
const Foo foo[] = {
{ 42, "the answer to the ultimate questions" },
{ 23 /*initializing only the first member, 's' gets the default value*/ }
};
What I want to do is to have a struct wrapping the array so I can add methods to it:
template<typename V1, typename V2, size_t Count>
struct Map
{
std::array<std::pair<V1, V2>, Count> mappings;
//or
//std::pair<V1, V2> mappings[Count];
V1 operator()(const V2&) const;
V2 operator()(const V1&) const;
};
And I want to initialize it as an array of unknown bound like this:
constexpr Map<int, std::string_view, /*???*/> = {
{ 42, "the answer to the ultimate question" },
{ 23, "some other stuff" },
{ /*...*/ }
};
But then a problem arises that you need to specify the Count
template parameter which I don't want to do, I want it to work like in the array case.
I thought that a function returning such object would do the trick, like this:
template<typename V1, typename V2, typename... Args>
constexpr auto makeMap(Args... args)
{
return Map<V1, V2, sizeof...(Args)>{ args... };
}
which then allows to use it like this:
using Item = std::pair<int, std::string_view>;
constexpr auto map = makeMap<int, std::string_view>(
Item{ 42, "the answer to the ultimate questions" },
Item{ 23, "some other stuff" }
);
But if you omit the Item
type, then template instantiation can not deduce argument types, which prohibits the usage that I originally wanted:
constexpr auto map = makeMap<int, std::string_view>(
{ 42, "the answer to the ultimate questions" },
{ 23, "some other stuff" }
);
Currently I consider it impossible, but wanted to ask anyway in case I'm missing something.
While researching this, I found a proposal which enables precisely what I want.
Anyways, I would love to get any ideas.
Using the proposed to_array
:
template<typename V1, typename V2, size_t N>
constexpr auto makeMap(std::pair<V1, V2> const (&a)[N])
{
return Map<V1, V2, N>{ to_array<std::pair<V1, V2>>(a) };
}
constexpr auto map = makeMap<int, std::string_view>({
{ 42, "the answer to the ultimate question" },
{ 23, "some other stuff" },
{ /*...*/ }
});
If your compiler supports library fundamentals TS v2, you can find an implementation of to_array
in header <experimental/array>
, inside namespace std::experimental
.
Following the Jarod42's suggestion, in a recursive way, I propose a recursive MakeMyMap
struct, with a static
func()
in it that receive a sequence of std::pair<T1, T2>
arguments [observe: 42 is the default upper bound for the number of std::pair
arguments].
template <typename T1, typename T2, std::size_t Dim>
struct MyMap
{
std::array<std::pair<T1, T2>, Dim> map;
};
template <typename T, std::size_t>
using getTheType = T;
template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;
template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
: public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
{
using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;
static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
{ return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
};
template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
{
static auto func ()
{ return MyMap<T1, T2, 0u>{ }; }
};
So you can write
auto map = MakeMyMap<int, std::string>::func(
{ 42, "the answer to the ultimate questions" },
{ 23, "some other stuff" }
);
The following is a full compiling (C++14 is enough) example
#include <array>
#include <string>
#include <utility>
template <typename T1, typename T2, std::size_t Dim>
struct MyMap
{
std::array<std::pair<T1, T2>, Dim> map;
};
template <typename T, std::size_t>
using getTheType = T;
template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;
template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
: public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
{
using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;
static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
{ return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
};
template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
{
static auto func ()
{ return MyMap<T1, T2, 0u>{ }; }
};
int main ()
{
auto map = MakeMyMap<int, std::string>::func(
{ 42, "the answer to the ultimate questions" },
{ 23, "some other stuff" }
);
static_assert( std::is_same<decltype(map),
MyMap<int, std::string, 2u>>::value, "!" );
}
Using C++17, you can use std::string_view
instead of std::string
, you can define constexpr
the func()
functions so map
can be constexpr
constexpr auto map = MakeMyMap<int, std::string_view>::func(
{ 42, "the answer to the ultimate questions" },
{ 23, "some other stuff" }
);
and you can also verify that
static_assert( std::is_same<decltype(map),
MyMap<int, std::string_view, 2u> const>::value, "!" );
Using the new C++17 variadic using
, you can avoid recursion at all rewiting MakeMyMap
as follows
template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;
template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
: public MMM_helper<T1, T2, std::make_index_sequence<Is>>...
{ using MMM_helper<T1, T2, std::make_index_sequence<Is>>::func...; };
where MMM_helper
(Make My Map helper) is defined as follows
template <typename, typename, typename>
struct MMM_helper;
template <typename T1, typename T2, std::size_t ... Is>
struct MMM_helper<T1, T2, std::index_sequence<Is...>>
{
static constexpr auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
{ return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
};
The following is a full C++17, not-recursive, example
#include <array>
#include <string_view>
#include <utility>
template <typename T1, typename T2, std::size_t Dim>
struct MyMap
{
std::array<std::pair<T1, T2>, Dim> map;
};
template <typename T, std::size_t>
using getTheType = T;
template <typename, typename, typename>
struct MMM_helper;
template <typename T1, typename T2, std::size_t ... Is>
struct MMM_helper<T1, T2, std::index_sequence<Is...>>
{
static constexpr auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
{ return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
};
template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;
template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
: public MMM_helper<T1, T2, std::make_index_sequence<Is>>...
{ using MMM_helper<T1, T2, std::make_index_sequence<Is>>::func...; };
int main ()
{
constexpr auto map = MakeMyMap<int, std::string_view>::func(
{ 42, "the answer to the ultimate questions" },
{ 23, "some other stuff" }
);
static_assert( std::is_same<decltype(map),
MyMap<int, std::string_view, 2u> const>::value, "!" );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With