Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/bicrementable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace strong
struct bicrementable
{
template <typename T>
class modifier
class STRONG_EMPTY_BASES modifier
: public incrementable::modifier<T>
, public decrementable::modifier<T>
{
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/convertible_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ template <typename ... Ts>
struct convertible_to
{
template <typename T>
struct modifier : impl::converter<T, Ts>...
struct STRONG_EMPTY_BASES modifier : impl::converter<T, Ts>...
{
};
};
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/difference.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ struct difference
};

template <typename T, typename Tag, typename ... M>
class difference::modifier<::strong::type<T, Tag, M...>>
class STRONG_EMPTY_BASES difference::modifier<::strong::type<T, Tag, M...>>
: public impl::conditionally_ordered::modifier<::strong::type<T, Tag, M...>>
, public equality::modifier<::strong::type<T, Tag, M...>>
{
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/equality_with.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ template <typename ... Ts>
struct equality_with
{
template <typename T>
class modifier : public impl::typed_equality<T, Ts>...
class STRONG_EMPTY_BASES modifier : public impl::typed_equality<T, Ts>...
{
};
};
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/implicitly_convertible_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ template <typename ... Ts>
struct implicitly_convertible_to
{
template <typename T>
struct modifier : impl::implicit_converter<T, Ts>...
struct STRONG_EMPTY_BASES modifier : impl::implicit_converter<T, Ts>...
{
};
};
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/iostreamable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace strong
struct iostreamable
{
template <typename T>
class modifier
class STRONG_EMPTY_BASES modifier
: public ostreamable::modifier<T>
, public istreamable::modifier<T>
{
Expand Down
10 changes: 5 additions & 5 deletions include/strong_type/iterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class iterator
public:
template <typename I,
typename category = typename internal::iterator_traits<underlying_type_t<I>>::iterator_category>
class modifier
class STRONG_EMPTY_BASES modifier
: public pointer::modifier<I>
, public incrementable::modifier<I>
{
Expand All @@ -84,18 +84,18 @@ class iterator
};

template <typename I>
class modifier<I, std::forward_iterator_tag>
class STRONG_EMPTY_BASES modifier<I, std::forward_iterator_tag>
: public modifier<I, std::input_iterator_tag>
, public strong::equality::modifier<I>
{};
template <typename I>
class modifier<I, std::bidirectional_iterator_tag>
class STRONG_EMPTY_BASES modifier<I, std::bidirectional_iterator_tag>
: public modifier<I, std::forward_iterator_tag>
, public decrementable::modifier<I>
{
};
template <typename I>
class modifier<I, std::random_access_iterator_tag>
class STRONG_EMPTY_BASES modifier<I, std::random_access_iterator_tag>
: public modifier<I, std::bidirectional_iterator_tag>
, public affine_point<typename std::iterator_traits<underlying_type_t<I>>::difference_type>::template modifier<I>
, public indexed<>::modifier<I>
Expand All @@ -105,7 +105,7 @@ class iterator

#if STRONG_TYPE_CONTIGUOUS_ITERATOR
template <typename I>
class modifier<I, std::contiguous_iterator_tag>
class STRONG_EMPTY_BASES modifier<I, std::contiguous_iterator_tag>
: public modifier<I, std::bidirectional_iterator_tag>
, public affine_point<typename std::iterator_traits<underlying_type_t<I>>::difference_type>::template modifier<I>
, public indexed<>::modifier<I>
Expand Down
4 changes: 2 additions & 2 deletions include/strong_type/ordered_with.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ template <typename ... Ts>
struct ordered_with
{
template <typename T>
class modifier : public impl::typed_ordering<T, Ts>...
class STRONG_EMPTY_BASES modifier : public impl::typed_ordering<T, Ts>...
{
};
};
Expand Down Expand Up @@ -166,7 +166,7 @@ template <typename Ordering, typename ... Ts>
struct spaceship_ordering_with
{
template <typename T>
struct modifier : public typed_spaceship_ordering_with<Ordering, T, Ts>...
struct STRONG_EMPTY_BASES modifier : public typed_spaceship_ordering_with<Ordering, T, Ts>...
{
};
};
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/regular.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace strong
struct regular
{
template <typename T>
class modifier
class STRONG_EMPTY_BASES modifier
: public semiregular::modifier<T>
, public equality::modifier<T>
{
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/scalable_with.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ template <typename ... Ts>
struct scalable_with
{
template <typename T>
class modifier : public impl::typed_scalable<T, Ts>...
class STRONG_EMPTY_BASES modifier : public impl::typed_scalable<T, Ts>...
{
public:
using scalable_modifier_result_type = impl::first_type_t<Ts...>;
Expand Down
2 changes: 1 addition & 1 deletion include/strong_type/semiregular.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct semiregular
};

template <typename T, typename Tag, typename ... M>
class semiregular::modifier<::strong::type<T, Tag, M...>>
class STRONG_EMPTY_BASES semiregular::modifier<::strong::type<T, Tag, M...>>
: public default_constructible::modifier<T>
, private impl::require_semiregular<T>
{
Expand Down
9 changes: 8 additions & 1 deletion include/strong_type/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
};
Expand Down Expand Up @@ -80,7 +87,7 @@ struct limiter<M, T, U, impl::void_t<decltype(M::template modifier<T>::limit(std
}

template<typename T, typename Tag, typename ... M>
class type : public modifier<M, type<T, Tag, M...>> ... {
class STRONG_EMPTY_BASES type : public modifier<M, type<T, Tag, M...>> ... {
public:
template<typename TT = T, typename = std::enable_if_t<std::is_trivially_constructible<TT>{}>>
explicit type(uninitialized_t)
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ add_executable(
empty.cpp
test_scalable_with.cpp
test_saturating.cpp
test_size.cpp
${CATCH_MAIN}
)

Expand Down
62 changes: 62 additions & 0 deletions test/test_size.cpp
Original file line number Diff line number Diff line change
@@ -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 <strong_type/regular.hpp>
#include <strong_type/equality.hpp>
#include <strong_type/ordered.hpp>
#include <strong_type/arithmetic.hpp>
#include <strong_type/bicrementable.hpp>
#include <strong_type/iostreamable.hpp>
#include <strong_type/difference.hpp>
#include <strong_type/equality_with.hpp>
#include <strong_type/ordered_with.hpp>
#include <strong_type/convertible_to.hpp>
#include <strong_type/scalable_with.hpp>

namespace {
// a single modifier: one empty base, folded by every ABI
using one_leaf = strong::type<int, struct one_leaf_, strong::equality>;

// several empty modifier bases: the case MSVC does not fold without help
using two_leaves = strong::type<int, struct two_leaves_, strong::equality, strong::ordered>;
using many_leaves = strong::type<double, struct many_leaves_, strong::arithmetic, strong::ordered, strong::equality>;

// composite modifiers, whose own modifier<T> has multiple empty bases
using is_regular = strong::type<int, struct is_regular_, strong::regular>;
using streamable = strong::type<int, struct streamable_, strong::iostreamable>;
using bicrement = strong::type<int, struct bicrement_, strong::bicrementable>;
using diff = strong::type<int, struct diff_, strong::difference>;

// variadic composite modifiers with more than one target type
using eq_with = strong::type<int, struct eq_with_, strong::equality_with<int, long>>;
using ord_with = strong::type<int, struct ord_with_, strong::ordered_with<int, long>>;
using conv_to = strong::type<int, struct conv_to_, strong::convertible_to<long, long long>>;
using scale_with = strong::type<int, struct scale_with_, strong::scalable_with<int, long>>;
}

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));
}