Skip to content

Commit 3721b1f

Browse files
committed
fix: work around msvc compiler bugs related to NTTP/array refs in param packs
It's unclear what exactly is going on here. But use of a reference to bounded-array as a template parameter inside a parameter pack cause MSVC to emite useless compiler errors. On MSVC 19.16 it appears to eliminate these overloads from consideration without any diagnostic (like SFINAE). On more recent versions it provides the supremely useless "failed to specialize function template" without informing about *why* it is failing to do so. And some variations of these, applying decltype to NTTPs, even ICEs the compiler. Converting `const T (&)[N]` to `array_ref<T, N>` allows us to *not* have a reference-to-array expression inside the parameter pack and this appears to stop triggering all compiler bugs discovered thus far.
1 parent 42cfcb6 commit 3721b1f

File tree

6 files changed

+229
-55
lines changed

6 files changed

+229
-55
lines changed

include/frozen/bits/basic_types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ struct size_integer {
6868
template <std::size_t total_size>
6969
using size_integer_t = typename size_integer<total_size>::type;
7070

71+
// Helper type to work around apparent compiler bugs in MSVC related to having "too complex"
72+
// template parameters involving either NTTP or references-to-array.
73+
template <class T, std::size_t N>
74+
struct array_ref {
75+
using array_type = const T(&) [N];
76+
using pointer = T*;
77+
78+
pointer array;
79+
80+
operator pointer () const noexcept { return array; }
81+
operator array_type() const noexcept { return array; }
82+
};
83+
7184
template <class T, std::size_t N>
7285
class cvector {
7386
public:

include/frozen/bits/pic_array.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -525,23 +525,23 @@ struct pic_array {
525525

526526
// Helpers to preserve arrays in type information, instead of letting them decay to pointers
527527
template <typename T, typename U, std::size_t TN, std::size_t UN>
528-
constexpr std::pair<T (&)[TN], U (&)[UN]> kv_pair(T (& key)[TN], U (& val)[UN]) {
529-
return {key, val};
528+
constexpr auto kv_pair(T (& key)[TN], U (& val)[UN]) {
529+
return std::pair<bits::array_ref<T, TN>, bits::array_ref<U, UN>>{{key}, {val}};
530530
}
531531

532532
template <typename T, typename U, std::size_t N>
533-
constexpr std::pair<T (&)[N], U> kv_pair(T (& key)[N], U val) {
534-
return {key, val};
533+
constexpr auto kv_pair(T (& key)[N], U val) {
534+
return std::pair<bits::array_ref<T, N>, U>{{key}, {val}};
535535
}
536536

537537
template <typename T, typename U, std::size_t N>
538-
constexpr std::pair<T, U (&)[N]> kv_pair(T key, U (& val)[N]) {
539-
return {key, val};
538+
constexpr auto kv_pair(T key, U (& val)[N]) {
539+
return std::pair<T, bits::array_ref<U, N>>{{key}, {val}};
540540
}
541541

542542
template <typename T, typename U>
543-
constexpr std::pair<T, U> kv_pair(T key, U val) {
544-
return {key, val};
543+
constexpr auto kv_pair(T key, U val) {
544+
return std::pair<T, U>{{key}, {val}};
545545
}
546546

547547
} // namespace frozen

include/frozen/map.h

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -361,55 +361,108 @@ constexpr auto make_map(std::array<std::pair<T, U>, N> const &items, Compare con
361361
return map<T, U, N, Compare>{items, compare};
362362
}
363363

364-
template <typename T, typename U, typename Compare, std::size_t... KNs,
365-
std::enable_if_t<!bits::is_pair<Compare>::value>* = nullptr>
364+
template <
365+
typename T
366+
, typename U
367+
, typename Compare
368+
, std::enable_if_t<
369+
!bits::is_pair<Compare>::value
370+
, std::size_t>... KNs
371+
>
366372
constexpr auto make_map(
367373
Compare const& compare,
368374
std::pair<
369-
bits::element_t<T> const (&)[KNs]
375+
bits::array_ref<const bits::element_t<T>, KNs>
370376
, U> const&... items) {
371377
constexpr const auto key_storage_size = bits::accumulate({KNs...});
372378
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(KNs), key_storage_size>;
373-
return map<T, U, sizeof...(KNs), Compare, container_type>{container_type{items...}, compare};
379+
using value_type = typename container_type::value_type;
380+
return map<T, U, sizeof...(KNs), Compare, container_type>{
381+
container_type{value_type(T(items.first.array), U(items.second))...},
382+
compare,
383+
};
374384
}
375385

376-
template <typename T, typename U, std::size_t... KNs>
377-
constexpr auto make_map(std::pair<bits::element_t<T> const (&)[KNs], U> const&... items) {
386+
template <
387+
typename T
388+
, typename U
389+
, std::size_t... KNs
390+
>
391+
constexpr auto make_map(
392+
std::pair<
393+
bits::array_ref<const bits::element_t<T>, KNs>
394+
, U> const&... items) {
378395
return make_map<T, U>(std::less<T>{}, items...);
379396
}
380397

381-
template <typename T, typename U, typename Compare, std::size_t... VNs,
382-
std::enable_if_t<!bits::is_pair<Compare>::value>* = nullptr>
398+
template <
399+
typename T
400+
, typename U
401+
, typename Compare
402+
, std::enable_if_t<
403+
!bits::is_pair<Compare>::value
404+
, std::size_t>... VNs
405+
>
383406
constexpr auto make_map(
384407
Compare const& compare,
385408
std::pair<
386409
T
387-
, bits::element_t<U> const (&)[VNs]> const&... items) {
410+
, bits::array_ref<const bits::element_t<U>, VNs>> const&... items) {
388411
constexpr const auto key_storage_size = bits::accumulate({VNs...});
389412
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(VNs), key_storage_size>;
390-
return map<T, U, sizeof...(VNs), Compare, container_type>{container_type{items...}, compare};
413+
using value_type = typename container_type::value_type;
414+
return map<T, U, sizeof...(VNs), Compare, container_type>{
415+
container_type{value_type(T(items.first), U(items.second.array))...},
416+
compare,
417+
};
391418
}
392419

393-
template <typename T, typename U, std::size_t... KNs>
394-
constexpr auto make_map(std::pair<T, bits::element_t<U> const (&)[KNs]> const&... items) {
420+
template <
421+
typename T
422+
, typename U
423+
, std::size_t... KNs
424+
>
425+
constexpr auto make_map(
426+
std::pair<
427+
T
428+
, bits::array_ref<const bits::element_t<U>, KNs>> const&... items) {
395429
return make_map<T, U>(std::less<T>{}, items...);
396430
}
397431

398-
template <typename T, typename U, typename Compare, std::size_t... KNs, std::size_t... VNs,
399-
std::enable_if_t<!bits::is_pair<Compare>::value>* = nullptr>
432+
template <
433+
typename T
434+
, typename U
435+
, typename Compare
436+
, std::size_t... KNs
437+
, std::enable_if_t<
438+
!bits::is_pair<Compare>::value
439+
, std::size_t>... VNs
440+
>
400441
constexpr auto make_map(
401442
Compare const& compare,
402443
std::pair<
403-
bits::element_t<T> const (&)[KNs]
404-
, bits::element_t<U> const (&)[VNs]> const&... items) {
444+
bits::array_ref<const bits::element_t<T>, KNs>
445+
, bits::array_ref<const bits::element_t<U>, VNs>> const&... items) {
405446
constexpr const auto key_storage_size = bits::accumulate({KNs...});
406447
constexpr const auto val_storage_size = bits::accumulate({VNs...});
407448
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(KNs), key_storage_size + val_storage_size>;
408-
return map<T, U, sizeof...(KNs), Compare, container_type>{container_type{items...}, compare};
449+
using value_type = typename container_type::value_type;
450+
return map<T, U, sizeof...(KNs), Compare, container_type>{
451+
container_type{value_type(T(items.first.array), U(items.second.array))...},
452+
compare,
453+
};
409454
}
410455

411-
template <typename T, typename U, std::size_t... KNs, std::size_t... VNs>
412-
constexpr auto make_map(std::pair<bits::element_t<T> const (&)[KNs], bits::element_t<U> const (&)[VNs]> const&... items) {
456+
template <
457+
typename T
458+
, typename U
459+
, std::size_t... KNs
460+
, std::size_t... VNs
461+
>
462+
constexpr auto make_map(
463+
std::pair<
464+
bits::array_ref<const bits::element_t<T>, KNs>
465+
, bits::array_ref<const bits::element_t<U>, VNs>> const&... items) {
413466
return make_map<T, U>(std::less<T>{}, items...);
414467
}
415468

include/frozen/unordered_map.h

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -251,77 +251,145 @@ constexpr auto make_unordered_map(
251251
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
252252
}
253253

254-
template <typename T, typename U, typename Hasher, typename Equal, std::size_t... KNs,
255-
std::enable_if_t<
256-
!bits::has_type<bits::element_type<U>>::value
257-
&& !bits::is_pair<Hasher>::value
258-
&& !bits::is_pair<Equal>::value>* = nullptr>
254+
template <
255+
typename T
256+
, typename U
257+
, typename Hasher
258+
, typename Equal
259+
, typename ElemT
260+
, std::enable_if_t<
261+
!bits::has_type<bits::element_type<U>>::value
262+
&& !bits::is_pair<Hasher>::value
263+
&& !bits::is_pair<Equal>::value
264+
&& std::is_same<ElemT, bits::element_t<T>>::value
265+
, std::size_t>... KNs
266+
>
259267
constexpr auto make_unordered_map(
260268
Hasher const &hash,
261269
Equal const &equal,
262270
std::pair<
263-
bits::element_t<T> const (&)[KNs]
271+
bits::array_ref<const ElemT, KNs>
264272
, U> const&... items) {
265273
constexpr const auto key_storage_size = bits::accumulate({KNs...});
266274
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(KNs), key_storage_size>;
267-
return unordered_map<T, U, sizeof...(KNs), Hasher, Equal, container_type>{container_type{items...}, hash, equal};
275+
using value_type = typename container_type::value_type;
276+
return unordered_map<T, U, sizeof...(KNs), Hasher, Equal, container_type>{
277+
container_type{value_type(T(items.first.array), U(items.second))...},
278+
hash,
279+
equal,
280+
};
268281
}
269282

270-
template <typename T, typename U, std::size_t... KNs,
271-
std::enable_if_t<!bits::has_type<bits::element_type<U>>::value>* = nullptr>
283+
template <
284+
typename T
285+
, typename U
286+
, typename ElemT
287+
, std::enable_if_t<
288+
!bits::has_type<bits::element_type<U>>::value
289+
&& std::is_same<ElemT, bits::element_t<T>>::value
290+
, std::size_t>... KNs
291+
>
272292
constexpr auto make_unordered_map(
273293
std::pair<
274-
bits::element_t<T> const (&)[KNs]
294+
bits::array_ref<const ElemT, KNs>
275295
, U> const&... items) {
276296
return make_unordered_map<T, U>(anna<T>{}, std::equal_to<T>{}, items...);
277297
}
278298

279-
template <typename T, typename U, typename Hasher, typename Equal, std::size_t... VNs,
280-
std::enable_if_t<
281-
!bits::has_type<bits::element_type<T>>::value
282-
&& !bits::is_pair<Hasher>::value
283-
&& !bits::is_pair<Equal>::value>* = nullptr>
299+
template <
300+
typename T
301+
, typename U
302+
, typename Hasher
303+
, typename Equal
304+
, typename ElemT
305+
, std::enable_if_t<
306+
!bits::has_type<bits::element_type<T>>::value
307+
&& !bits::is_pair<Hasher>::value
308+
&& !bits::is_pair<Equal>::value
309+
&& std::is_same<ElemT, bits::element_t<U>>::value
310+
, std::size_t>... VNs
311+
>
284312
constexpr auto make_unordered_map(
285313
Hasher const &hash,
286314
Equal const &equal,
287315
std::pair<
288316
T
289-
, bits::element_t<U> const (&)[VNs]
317+
, bits::array_ref<const ElemT, VNs>
290318
> const&... items) {
291319
constexpr const auto val_storage_size = bits::accumulate({VNs...});
292320
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(VNs), val_storage_size>;
293-
return unordered_map<T, U, sizeof...(VNs), Hasher, Equal, container_type>{container_type{items...}, hash, equal};
321+
using value_type = typename container_type::value_type;
322+
return unordered_map<T, U, sizeof...(VNs), Hasher, Equal, container_type>{
323+
container_type{value_type(T(items.first), U(items.second.array))...},
324+
hash,
325+
equal,
326+
};
294327
}
295328

296-
template <typename T, typename U, std::size_t... VNs,
297-
std::enable_if_t<!bits::has_type<bits::element_type<T>>::value>* = nullptr>
329+
template <
330+
typename T
331+
, typename U
332+
, typename ElemT
333+
, std::enable_if_t<
334+
!bits::has_type<bits::element_type<T>>::value
335+
&& std::is_same<ElemT, bits::element_t<U>>::value
336+
, std::size_t>... VNs
337+
>
298338
constexpr auto make_unordered_map(
299339
std::pair<
300340
T
301-
, bits::element_t<U> const (&)[VNs]
341+
, bits::array_ref<const ElemT, VNs>
302342
> const&... items) {
303343
return make_unordered_map<T, U>(anna<T>{}, std::equal_to<T>{}, items...);
304344
}
305345

306-
template <typename T, typename U, typename Hasher, typename Equal, std::size_t... KNs, std::size_t... VNs,
307-
std::enable_if_t<!bits::is_pair<Hasher>::value && !bits::is_pair<Equal>::value>* = nullptr>
346+
template <
347+
typename T
348+
, typename U
349+
, typename Hasher
350+
, typename Equal
351+
, typename ElemT
352+
, typename ElemU
353+
, std::size_t... KNs
354+
, std::enable_if_t<
355+
!bits::is_pair<Hasher>::value
356+
&& !bits::is_pair<Equal>::value
357+
&& std::is_same<ElemT, bits::element_t<T>>::value
358+
&& std::is_same<ElemU, bits::element_t<U>>::value
359+
, std::size_t>... VNs
360+
>
308361
constexpr auto make_unordered_map(
309362
Hasher const &hash,
310363
Equal const &equal,
311364
std::pair<
312-
bits::element_t<T> const (&)[KNs]
313-
, bits::element_t<U> const (&)[VNs]
365+
bits::array_ref<const ElemT, KNs>
366+
, bits::array_ref<const ElemU, VNs>
314367
> const&... items) {
315368
constexpr const auto key_storage_size = bits::accumulate({KNs...});
316369
constexpr const auto val_storage_size = bits::accumulate({VNs...});
317370
using container_type = bits::pic_array<std::pair<const T, U>, sizeof...(KNs), key_storage_size + val_storage_size>;
318-
return unordered_map<T, U, sizeof...(KNs), Hasher, Equal, container_type>{container_type{items...}, hash, equal};
371+
using value_type = typename container_type::value_type;
372+
return unordered_map<T, U, sizeof...(KNs), Hasher, Equal, container_type>{
373+
container_type{value_type(T(items.first.array), U(items.second.array))...},
374+
hash,
375+
equal,
376+
};
319377
}
320378

321-
template <typename T, typename U, std::size_t... KNs, std::size_t... VNs>
379+
template <
380+
typename T
381+
, typename U
382+
, typename ElemT
383+
, typename ElemU
384+
, std::size_t... KNs
385+
, std::enable_if_t<
386+
std::is_same<ElemT, bits::element_t<T>>::value
387+
&& std::is_same<ElemU, bits::element_t<U>>::value
388+
, std::size_t>... VNs
389+
>
322390
constexpr auto make_unordered_map(std::pair<
323-
bits::element_t<T> const (&)[KNs]
324-
, bits::element_t<U> const (&)[VNs]
391+
bits::array_ref<const ElemT, KNs>
392+
, bits::array_ref<const ElemU, VNs>
325393
> const&... items) {
326394
return make_unordered_map<T, U>(anna<T>{}, std::equal_to<T>{}, items...);
327395
}

tests/test_map.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,23 @@ TEST_CASE("frozen::map <> frozen::make_map transparent", "[map]") {
499499
REQUIRE(frozen_empty_map3.begin() == frozen_empty_map3.end());
500500
}
501501
}
502+
503+
TEST_CASE("frozen::make_map variations with frozen::string", "[map]") {
504+
using frozen::kv_pair;
505+
506+
constexpr auto si = frozen::make_map<frozen::string, int>(
507+
kv_pair("a", 1)
508+
, kv_pair("b", 2)
509+
);
510+
constexpr auto is = frozen::make_map<int, frozen::string>(
511+
kv_pair(1, "a")
512+
, kv_pair(2, "b")
513+
);
514+
constexpr auto ss = frozen::make_map<frozen::string, frozen::string>(
515+
kv_pair("1", "a")
516+
, kv_pair("2", "b")
517+
);
518+
519+
static_assert(is.at(si.at("a")) == "a", "");
520+
static_assert(ss.at("1") == "a", "");
521+
}

0 commit comments

Comments
 (0)