From aba6053411db7a27864bcdf26ebe8c9da6bf2705 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Fri, 10 Jul 2026 14:46:45 +0300 Subject: [PATCH] Fix missing empty base optimization on MSVC --- ChangeLog | 2 + include/strong_type/bicrementable.hpp | 2 +- include/strong_type/convertible_to.hpp | 2 +- include/strong_type/difference.hpp | 2 +- include/strong_type/equality_with.hpp | 2 +- .../strong_type/implicitly_convertible_to.hpp | 2 +- include/strong_type/iostreamable.hpp | 2 +- include/strong_type/iterator.hpp | 10 +-- include/strong_type/ordered_with.hpp | 4 +- include/strong_type/regular.hpp | 2 +- include/strong_type/scalable_with.hpp | 2 +- include/strong_type/semiregular.hpp | 2 +- include/strong_type/type.hpp | 9 ++- test/CMakeLists.txt | 1 + test/test_size.cpp | 62 +++++++++++++++++++ 15 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 test/test_size.cpp diff --git a/ChangeLog b/ChangeLog index 9bb7339..8625ac5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * Fixed missing empty base optimization on MSVC. + * Added strong::saturating_arithmetic for C++26. * Support for "import std;" is enabled by defining the macro diff --git a/include/strong_type/bicrementable.hpp b/include/strong_type/bicrementable.hpp index 9b0d06c..e01d81e 100644 --- a/include/strong_type/bicrementable.hpp +++ b/include/strong_type/bicrementable.hpp @@ -22,7 +22,7 @@ namespace strong struct bicrementable { template - class modifier + class STRONG_EMPTY_BASES modifier : public incrementable::modifier , public decrementable::modifier { diff --git a/include/strong_type/convertible_to.hpp b/include/strong_type/convertible_to.hpp index 1e0423c..2440d49 100644 --- a/include/strong_type/convertible_to.hpp +++ b/include/strong_type/convertible_to.hpp @@ -47,7 +47,7 @@ template struct convertible_to { template - struct modifier : impl::converter... + struct STRONG_EMPTY_BASES modifier : impl::converter... { }; }; diff --git a/include/strong_type/difference.hpp b/include/strong_type/difference.hpp index e6ca121..c080a97 100644 --- a/include/strong_type/difference.hpp +++ b/include/strong_type/difference.hpp @@ -92,7 +92,7 @@ struct difference }; template -class difference::modifier<::strong::type> +class STRONG_EMPTY_BASES difference::modifier<::strong::type> : public impl::conditionally_ordered::modifier<::strong::type> , public equality::modifier<::strong::type> { diff --git a/include/strong_type/equality_with.hpp b/include/strong_type/equality_with.hpp index f1c928e..6b01699 100644 --- a/include/strong_type/equality_with.hpp +++ b/include/strong_type/equality_with.hpp @@ -80,7 +80,7 @@ template struct equality_with { template - class modifier : public impl::typed_equality... + class STRONG_EMPTY_BASES modifier : public impl::typed_equality... { }; }; diff --git a/include/strong_type/implicitly_convertible_to.hpp b/include/strong_type/implicitly_convertible_to.hpp index 2e8018f..4dedd99 100644 --- a/include/strong_type/implicitly_convertible_to.hpp +++ b/include/strong_type/implicitly_convertible_to.hpp @@ -46,7 +46,7 @@ template struct implicitly_convertible_to { template - struct modifier : impl::implicit_converter... + struct STRONG_EMPTY_BASES modifier : impl::implicit_converter... { }; }; diff --git a/include/strong_type/iostreamable.hpp b/include/strong_type/iostreamable.hpp index 8f21b4f..7f47098 100644 --- a/include/strong_type/iostreamable.hpp +++ b/include/strong_type/iostreamable.hpp @@ -22,7 +22,7 @@ namespace strong struct iostreamable { template - class modifier + class STRONG_EMPTY_BASES modifier : public ostreamable::modifier , public istreamable::modifier { diff --git a/include/strong_type/iterator.hpp b/include/strong_type/iterator.hpp index a76acec..3493293 100644 --- a/include/strong_type/iterator.hpp +++ b/include/strong_type/iterator.hpp @@ -71,7 +71,7 @@ class iterator public: template >::iterator_category> - class modifier + class STRONG_EMPTY_BASES modifier : public pointer::modifier , public incrementable::modifier { @@ -84,18 +84,18 @@ class iterator }; template - class modifier + class STRONG_EMPTY_BASES modifier : public modifier , public strong::equality::modifier {}; template - class modifier + class STRONG_EMPTY_BASES modifier : public modifier , public decrementable::modifier { }; template - class modifier + class STRONG_EMPTY_BASES modifier : public modifier , public affine_point>::difference_type>::template modifier , public indexed<>::modifier @@ -105,7 +105,7 @@ class iterator #if STRONG_TYPE_CONTIGUOUS_ITERATOR template - class modifier + class STRONG_EMPTY_BASES modifier : public modifier , public affine_point>::difference_type>::template modifier , public indexed<>::modifier diff --git a/include/strong_type/ordered_with.hpp b/include/strong_type/ordered_with.hpp index ed64dd4..4eaa875 100644 --- a/include/strong_type/ordered_with.hpp +++ b/include/strong_type/ordered_with.hpp @@ -131,7 +131,7 @@ template struct ordered_with { template - class modifier : public impl::typed_ordering... + class STRONG_EMPTY_BASES modifier : public impl::typed_ordering... { }; }; @@ -166,7 +166,7 @@ template struct spaceship_ordering_with { template - struct modifier : public typed_spaceship_ordering_with... + struct STRONG_EMPTY_BASES modifier : public typed_spaceship_ordering_with... { }; }; diff --git a/include/strong_type/regular.hpp b/include/strong_type/regular.hpp index c44b494..a013a42 100644 --- a/include/strong_type/regular.hpp +++ b/include/strong_type/regular.hpp @@ -22,7 +22,7 @@ namespace strong struct regular { template - class modifier + class STRONG_EMPTY_BASES modifier : public semiregular::modifier , public equality::modifier { diff --git a/include/strong_type/scalable_with.hpp b/include/strong_type/scalable_with.hpp index 51f9b08..2fbe9a7 100644 --- a/include/strong_type/scalable_with.hpp +++ b/include/strong_type/scalable_with.hpp @@ -107,7 +107,7 @@ template struct scalable_with { template - class modifier : public impl::typed_scalable... + class STRONG_EMPTY_BASES modifier : public impl::typed_scalable... { public: using scalable_modifier_result_type = impl::first_type_t; diff --git a/include/strong_type/semiregular.hpp b/include/strong_type/semiregular.hpp index 832abcd..5ddf116 100644 --- a/include/strong_type/semiregular.hpp +++ b/include/strong_type/semiregular.hpp @@ -38,7 +38,7 @@ struct semiregular }; template -class semiregular::modifier<::strong::type> +class STRONG_EMPTY_BASES semiregular::modifier<::strong::type> : public default_constructible::modifier , private impl::require_semiregular { diff --git a/include/strong_type/type.hpp b/include/strong_type/type.hpp index 5626b47..162e0ef 100644 --- a/include/strong_type/type.hpp +++ b/include/strong_type/type.hpp @@ -35,6 +35,13 @@ #define STRONG_NODISCARD #endif +// the MSVC ABI does not collapse multiple empty base classes unless asked to +#if defined(_MSC_VER) +#define STRONG_EMPTY_BASES __declspec(empty_bases) +#else +#define STRONG_EMPTY_BASES +#endif + namespace strong { struct uninitialized_t { }; @@ -80,7 +87,7 @@ struct limiter::limit(std } template -class type : public modifier> ... { +class STRONG_EMPTY_BASES type : public modifier> ... { public: template{}>> explicit type(uninitialized_t) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5e5b3b9..a8f3c0d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -98,6 +98,7 @@ add_executable( empty.cpp test_scalable_with.cpp test_saturating.cpp + test_size.cpp ${CATCH_MAIN} ) diff --git a/test/test_size.cpp b/test/test_size.cpp new file mode 100644 index 0000000..0dbcefc --- /dev/null +++ b/test/test_size.cpp @@ -0,0 +1,62 @@ +/* + * strong_type C++14/17/20 strong typedef library + * + * Copyright (C) Björn Fahller + * + * Use, modification and distribution is subject to the + * Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * Project home: https://github.com/rollbear/strong_type + */ + +#include "catch2.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +// a single modifier: one empty base, folded by every ABI +using one_leaf = strong::type; + +// several empty modifier bases: the case MSVC does not fold without help +using two_leaves = strong::type; +using many_leaves = strong::type; + +// composite modifiers, whose own modifier has multiple empty bases +using is_regular = strong::type; +using streamable = strong::type; +using bicrement = strong::type; +using diff = strong::type; + +// variadic composite modifiers with more than one target type +using eq_with = strong::type>; +using ord_with = strong::type>; +using conv_to = strong::type>; +using scale_with = strong::type>; +} + +TEST_CASE("a strong::type is the same size as its underlying type") +{ + STATIC_REQUIRE(sizeof(one_leaf) == sizeof(int)); + STATIC_REQUIRE(sizeof(two_leaves) == sizeof(int)); + STATIC_REQUIRE(sizeof(many_leaves) == sizeof(double)); + STATIC_REQUIRE(sizeof(is_regular) == sizeof(int)); + STATIC_REQUIRE(sizeof(streamable) == sizeof(int)); + STATIC_REQUIRE(sizeof(bicrement) == sizeof(int)); + STATIC_REQUIRE(sizeof(diff) == sizeof(int)); + STATIC_REQUIRE(sizeof(eq_with) == sizeof(int)); + STATIC_REQUIRE(sizeof(ord_with) == sizeof(int)); + STATIC_REQUIRE(sizeof(conv_to) == sizeof(int)); + STATIC_REQUIRE(sizeof(scale_with) == sizeof(int)); +}