Am 25.08.2012 21:23, schrieb James Kuyper:
???
The claim was that use of C was a necessity, not just a convenience. C
doesn't have constructors, and designated initializers are only a
convenience feature that don't do anything that can't already be done
using ordinary initializers - they just make it more convenient.
How would you do
unsigned char errVector[] = { [EINVAL] = true, [ERANGE] = true, };
enum { maxNeeded = sizeof(errVector), };
in C++ ?
(I apologize for this bunch of C++ in a technically unrelated group,
for those offended.)
This question caught my attention: can this be done? This certainly
falls into the "there's many a way to do this" basket in C++, and
perhaps (like mostly anything) with enough Boost magic and repetitive
overloads, it has always been possible; however, I sought for a
(relatively) expressive solution using the relatively powerful
variadic templates, and came up with this. (Someone better than me
might come up with something cleaner, as most would agree that this
*is* rather wieldy.)
#include <array>
#include <cstddef>
#include <type_traits>
template<std::size_t i, std::size_t... Rest>
struct indices {
typedef typename std::enable_if<sizeof...(Rest) != 0,
indices<Rest...>>::type next;
static constexpr std::size_t value = i,
max = (value + 1 > next::max)
? value + 1
: next::max;
};
template<std::size_t i>
struct indices<i> {
typedef void next;
static constexpr std::size_t value = i,
max = value + 1;
};
template<class U, std::size_t N, class T>
constexpr typename std::enable_if<std::is_void<T>::value, U>::type
get()
{
return U();
}
template<class U, std::size_t N, class T, class V, class... Rest>
constexpr typename std::enable_if<!std::is_void<T>::value, U>::type
get(V&& v, Rest&&... rest)
{
return (T::value == N)
? std::forward<V>(v)
: get<U, N, typename T::next>(std::forward<Rest>(rest)...);
}
template<class U, class Indices, std::size_t... Gen, class... Rest>
constexpr typename std::enable_if<sizeof...(Gen) == Indices::max,
std::array<U, Indices::max>>::type
make_(Rest&&... rest)
{
return {{get<U, Gen, Indices>(std::forward<Rest>(rest)...)...}};
}
template<class U, class Indices, std::size_t... Gen, class... Rest>
constexpr typename std::enable_if<sizeof...(Gen) != Indices::max,
std::array<U, Indices::max>>::type
make_(Rest&&... rest)
{
return make_<U, Indices, Gen..., sizeof...(Gen)>(
std::forward<Rest>(rest)...);
}
template<class U, std::size_t... Indices, class... Rest>
constexpr std::array<U, indices<Indices...>::max> make(Rest&&... rest)
{
return make_<U, indices<Indices...>>(std::forward<Rest>(rest)...);
}
.... and what does all of this accomplish? Not a whole lot; at least
not without something similar to the code below (this is a scenario
from a later reply, because the one above seemed too simplistic.)
auto errVector = make<const char*, EBADF, 0, ERANGE>(
"EBADF", "(success)", "ERANGE");
enum { maxNeeded = sizeof(errVector) };
'errVector' above is actually not an array, but rather a std::array
object. It's unavoidable, but luckily std::array is just a very simple
wrapper of one.
One could perhaps achieve something to this end with structs, too,
although the syntax might be relatively even more awkward. (It's
certain that the members would have to be "listed" in some way as
there's no other way to get to know of them.) I'm not 100% certain
about this being doable, however.
....
Now that we've come this far, intuition begs the question: do the C++
folk actually believe they've got the superior tools when they refuse
to incorporate designated initializers into the language, when it's
obvious to anyone that they don't (have them).
I don't think the situation is that simple.
The way I see it, the co-operation of the committees will mostly
revolve around and result in not introducing subtle incompabilities
(the ones that won't show up when compiling) into the languages; and
not in extending the usable common subset of them. Static
initialization, I think, is often simply seen as less important within
C++ circles, because with complex objects you have lots to do at
runtime anyway.
By the way, with only a few values far between, one might want to use
an associative container instead of a mostly-wasted array. C++ has
relatively expressive syntax for those (shown below), but I don't
think C is getting anything similar anytime soon, tricks or not.
std::unordered_map<int, const char*> errMap = { { ERANGE, "ERANGE" },
{ 0, "(success)" }
/* ... */
};