Giter VIP home page Giter VIP logo

type_safe's Introduction

type_safe

Project Status Build Status

type_safe provides zero overhead abstractions that use the C++ type system to prevent bugs.

Zero overhead abstractions here and in following mean abstractions that have no cost with optimizations enabled, but may lead to slightly lower runtime in debug mode, especially when assertions for this library are enabled.

The library features cannot really be explained in the scope of this readme, I highly suggest that you check out the first and second blog post and the examples.

Features

Improved built-in types

  • ts::integer<T> - a zero overhead wrapper over a built-in integer type
    • no default constructor to force meaningful initialization
    • no "lossy" conversions (i.e. from a bigger type or a type with a different signedness)
    • no mixed arithmetic with floating points or integer types of a different signedness
    • no mixed comparison with floating points
    • over/underflow is undefined behavior in release mode - even for unsigned integers, enabling compiler optimizations
  • ts::floating_point<T> - a zero overhead wrapper over a built-in floating point
    • no default constructor to force meaningful initialization
    • no "lossy" conversion (i.e. from a bigger type)
    • no "lossy" comparisons
    • no mixed arithmetic/comparison with integers
  • ts::boolean - a zero overhead wrapper over bool
    • no default constructor to force meaningful initialization
    • no conversion from integer values
    • no arithmetic operators
  • aliases like ts::uint32_t or ts::size_t that are either wrapper or built-in type depending on macro
  • literal operators for those aliases like 342_u32 or 0_usize

Vocabulary types

  • ts::object_ref<T> - a non-null pointer
  • ts::index_t and ts::distance_t - index and distance integer types with only a subset of operations available
  • ts::array_ref<T> - non-null reference to contigous storage
  • ts::function_ref<T> - non-null reference to a function
  • ts::flag - an improved flag type, better than a regular bool or ts::boolean
  • ts::flag_set<Enum> - a set of flags
  • ts::output_parameter<T> - an improved output parameter compared to the naive lvalue reference

Optional & Variant

  • ts::basic_optional<StoragePolicy> - a generic, improved std::optional that is fully monadic, also ts::optional<T> and ts::optional_ref<T> implementations
  • ts::compact_optional implementation for no space overhead optionals
  • ts::basic_variant<VariantPolicy, Types...> - a generic, improved std::variant, also ts::variant and ts::fallback_variant implementations

Type safe building blocks

  • ts::constrained_type<T, Constraint, Verifier> - a wrapper over some type that verifies that a certain constraint is always fulfilled
    • ts::constraints::* - predefined constraints like non_null, non_empty, ...
    • ts::tagged_type<T, Constraint> - constrained type without checking, useful for tagging
    • ts::bounded_type<T> - constrained type that ensures a value in a certain interval
    • ts::clamped_type<T> - constrained type that clamps a value to ensure that it is in the certain interval
  • ts::strong_typedef - a generic facility to create strong typedefs more easily
  • ts::deferred_construction<T> - create an object without initializing it yet

Installation

Header-only, just copy the files in your project. You need to add the type_safe include directory to your include path as well as make debug_assert.hpp available. The repository is included. You also need to enable C++11.

Behavior can be customized with the following macros:

  • TYPE_SAFE_ENABLE_ASSERTIONS (default is 1): whether or not assertions are enabled in this library
  • TYPE_SAFE_ENABLE_WRAPPER (default is 1): whether or not the typedefs in type_safe/types.hpp use the wrapper classes
  • TYPE_SAFE_ARITHMETIC_POLICY (ub/checked/default, default is ub): whether under/overflow in the better integer types is UB, an exception, or the default behavior

If you're using CMake there is the target type_safe available after you've called add_subdirectory(path/to/type_safe). Simply link this target to your target and it will setup everything automagically. For convenience the macros are also mapped to CMake options of the same name.

Documentation

You can find the full documentation generated by standardese here.

Acknowledgements

This project is greatly supported by my patrons. In particular thanks to the individual supporters:

  • Mark Atkinson
  • Reiner Eiteljörge

And big thanks to the main contributors as well:

type_safe's People

Contributors

brevzin avatar brukerjwd avatar cstratopoulos avatar davidhunter22 avatar dvirtz avatar effzeh avatar foonathan avatar gerboengels avatar jeromehue avatar johelegp avatar kyku avatar ldalessa avatar life4gal avatar lisongmin avatar mandarjkulkarni avatar manu343726 avatar marco-langer avatar mathysie avatar meikjaeckle avatar nicola-gigante avatar nn--- avatar pfultz2 avatar rolandschulz avatar sdebionne avatar szekipepe avatar user-simon avatar verri avatar xenoamor avatar xtofl avatar zeromemes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

type_safe's Issues

`type_safe::visit` doesn't return references

Consider the following minimal example:

#include <type_safe/variant.hpp>
#include <type_safe/visitor.hpp>

int main()
{
    type_safe::variant<int> x{1};
    &type_safe::visit(
        [](int) -> int& {
            int a;
            return a;
        },
        x);
}

It doesn't compile on neither g++7 nor clang++5:

error: lvalue required as unary ‘&’ operand
         x);
          ^

For some reason the visit call is returning int instead of int&.

type_safe::variant<int> x{1};
decltype(auto) y = type_safe::visit(
    [](int) -> int& {
        int a;
        return a;
    },
    x);

    static_assert(std::is_same_v<decltype(y), int&>); // FAILS
    static_assert(std::is_same_v<decltype(y), int>);  // PASSES

Verifier, noexcept and exceptions

I believe that either the Verifier concept used in constrained_type is underspecified or the latter's functions conditionally marked noexcept on is_nothrow_* are incorrect. This is because there's no restriction on Verifier::verify() to not throw an exception. If that happens in the mentioned functions, std::terminate would be called, which is probably undesirable.

EqualityComparable and TotallyOrdered `constrained_type`

Given that constrained_type is a wrapper for a T, it'd be very convenient if it modelled the EqualityComparable and TotallyOrdered concepts if T does. This relieves users from doing hack-arounds to get their comparisons done.

It should be type-safe, by default, to compare objects of the same constrained_type specialization.

Other considerations:

  • Comparison with const U& u, if constructing the constrained_type from u is well-formed.
  • Comparison with other constrained_type of T, regardless of their Constraint and Verifier.

Unintuitive error messages with ts::boolean

Suppose I have a class that uses a ts::strong_typedef with equality_comparison:

struct memory_address :
  type_safe::strong_typedef<memory_address, type_safe::uint32_t>,
  type_safe::strong_typedef_op::equality_comparison<memory_address>,
  type_safe::strong_typedef_op::output_operator<memory_address>
{
  using strong_typedef::strong_typedef;
};

class example1 {
public:
  example1(memory_address const obj);
private:
  memory_address const object;

  friend bool operator==(example1 const &, example1 const &);
};

The following will not compile (because it cannot convert type_safe::boolean to bool - I am aware I could use equality_comparison<memory_address, bool> instead):

inline bool operator==(example1 const & lhs, example1 const & rhs)
{
  return lhs.object == rhs.object;
}

However, in a second example class we add another comparison using type_safe::integer, which will compile just fine:

class example2 {
public:
  example2(memory_address const obj, unsigned const count);
private:
  memory_address const object;
  type_safe::integer<unsigned> count;

  friend bool operator==(example2 const &, example2 const &);
};

inline bool operator==(example2 const & lhs, example2 const & rhs)
{
  return lhs.object == rhs.object && lhs.count == rhs.count;
}

Should this not have also caused an error?

Idea: Safe wrapper with overhead

Hi there,

@pirapira and me are working on a concept of an integer wrapper called "safe" that extends the values of an integer type with 3 special values: minus infinity (-Inf), plus infinity (+Inf), invalid (Invalid). We want to detect overflows and convert the results to these special values.

Example:

bool test(int a, int b) {
  safe<int> x = a;
  safe<int> y = b;
  auto r = x + y;  // This can be normal value, -Inf or +Inf. No UB.
  return r > 0;  // Always valid, because +Inf is greater than 0 and -Inf is less than 0.
}

If we can prove this math is consistent, I wanted to ship this as a small header only library. I wander if you would like to include it here instead.

hash specialization error

This may be entirely user-error, but I’m not figuring it out.

I am using type_safe in my streams project. When I try to compile, I’m getting this error:

clang++ -std=c++14 -stdlib=libc++ -I../../GSL -I../../fmt -I../../type_safe/include -I../../debug_assert -I..  -L../../fmt/build/fmt -lfmt  examples.cpp   -o examples
In file included from examples.cpp:4:
In file included from ../streams/istream.hpp:5:
../../type_safe/include/type_safe/optional.hpp:1039:11: error: explicit
      specialization of non-template class 'hash'
    class hash<type_safe::basic_optional<StoragePolicy>>
          ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
make: *** [examples] Error 1

Which is strange because hash clearly is a template class.

If I #if 0 out the entire namespace std section in type_safe/optional.hpp, everything compiles fine.

I’m using Clang 3.8.1. (Although I am on Mac OS, I’m not using Apple’s Clang. Just the regular 3.8.1.)

$ clang++ --version
clang version 3.8.1 (tags/RELEASE_381/final)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
InstalledDir: /opt/local/libexec/llvm-3.8/bin

Any ideas would be appreciated.

Avoiding download of debug_assert

How to tell the cmake code to avoid downloading debug_assert.hpp as a git submodule because I already use it in my project and the target already exists?

Doing something like that raises an error:

add_subdirectory(external/debug_assert)
add_subdirectory(external/type_safe)

Checking that `bounded`'s bounds are `T`

I noticed that in the simplification of the original implementation of the stateless bounded, the checks that the static and dynamic bounds are T was lost. I don't know if it was intentional for the dynamic bounds, but its problematic for the static bounds because the comparison constraints return a const T&. I also noticed that the comparison constraints never checked that its static bound was T.

Enabled `std::hash` for `strong_typedef`s with `strong_typedef_op::hashable`

Greetings.

I just found myself specializing std::hash for some strong typedefs of mine which are provided for convenience. I thought it'd be great if the library provided a strong_typedef_op that enabled it.

I am available to write it. Would the name in the title be ok? How about copying the hash tests of other library components?

Compile even with "-fno-exception"

I am trying to use the library in a project that compiles without exception support.
The only problem seems to be in include/type_safe/arithmetic_policy.hpp where a couple of exceptions are thrown..

My current workaround is adding the following macro in the above file:

#if (!defined(__EXCEPTIONS)) && defined(__GNUC__)
    #define TYPE_SAFE_THROW_XOR_ASSERT(x) DEBUG_UNREACHABLE(detail::assert_handler{}, x)
#else
    #define TYPE_SAFE_THROW_XOR_ASSERT(x) throw error (x)
#endif

__EXCEPTIONS is defined by gcc and clang, I do not know about other compiles, so I added the GNUC check, too.
Then I changed the throws from:

    throw error ("addition will result in overflow") :

to:

    TYPE_SAFE_THROW_XOR_ASSERT ("addition will result in overflow") :

Would you consider adding something similar?

Renaming {Lower,Upper}Constant to {Lower,Upper}Bound

99dd91c changed the naming of what was {Lower,Upper}Constant to {Lower,Upper}Bound. I like this new naming because it naturally includes dynamic_bound and doesn't exclude them being "constant". The previous naming seemed unnatural when a Constant was assigned something named dynamic. So I suggest renaming these template arguments.

an integration test suite could be useful

It's great there are already a number of CI jobs defined. It would be greater still to have some tests that show how an integrator of the library would use it.

This will trap 'physical design' errors like missing/misplaced include files, duplicate definitions, ...

Currently, for instance, including all headers together will result in a double definition

cl /I../../include /I../../external/debug_assert include_all.cpp
E:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xlocale(341): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
..\..\include\type_safe/strong_typedef.hpp(106): error C2086: 'type_safe::detail::underlying_type = underlying_type_impl<Enum>::type': redefinition
..\..\include\type_safe/compact_optional.hpp(280): note: see declaration of 'type_safe::detail::underlying_type'

using xvalue as lvalue

I just noticed that in your macro TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND(...) you wrote two operators that use an xvalue as an lvalue (which ofc results in a compile error when used):

// ...
/** \exclude */                                                                                \
friend StrongTypedef&& operator Op(StrongTypedef&& lhs, const Other& rhs)                      \
{                                                                                              \
	using type = underlying_type<StrongTypedef>;                                               \
	std::move(static_cast<type&>(lhs)) Op static_cast<const type&>(rhs);                       \
	return std::move(lhs);                                                                     \
}                                                                                              \
/** \exclude */                                                                                \
friend StrongTypedef&& operator Op(StrongTypedef&& lhs, Other&& rhs)                           \
{                                                                                              \
	using type = underlying_type<StrongTypedef>;                                               \
	std::move(static_cast<type&>(lhs)) Op std::move(static_cast<type&>(rhs));                  \
	return std::move(lhs);                                                                     \
}

My suggestion is to leave out the std::move() from the casted lhs where Op is applied.

type_safe always exposes target debug_assert_example

Adding type_safe library to one's project using CMake always adds target debug_assert_example to the list of all targets. The list is visible for instance in Qt Creator with Ctrl+T shortcut (or clicking in the lower right corner of IDE, above Run icon).

Example:

cmake_minimum_required(VERSION 3.0)
project(type-safe-expose-example)
add_subdirectory(external/type_safe)
add_executable(${PROJECT_NAME} "main.cpp")

Other libraries that I've seen allow omitting custom library targets by setting definitions, i.e. Catch2 creates example targets only if BUILD_EXAMPLES option is set (and by default it's off). Can you do something similar to not pollute user's project targets? Thanks in advance.

Interaction between ts::strong_typedef and ts::integer

This is a clarification more than a bug. I'm struggling understanding if and how ts::strong_typedef and ts::integer typedefs are intended to be used together. Can ts::strong_typedef be instantiated with e.g. ts::size_t as an underlying type?

Consider this code:

#include <type_safe/strong_typedef.hpp>
#include <type_safe/types.hpp>
#include <type_safe/index.hpp>

namespace ts = type_safe;

template<typename T>
struct bitblock
    : ts::strong_typedef<bitblock<T>, T>,
      ts::strong_typedef_op::bitmask<bitblock<T>>
{
    using ts::strong_typedef<bitblock<T>,T>::strong_typedef;
};

int main()
{
  bitblock<ts::size_t> bits{~0u};
  bitblock<ts::size_t> mask{0x00400u};

  bits = bits & mask;

  return 0;
}

Apple Clang 8.1 (damn apple for those useless version numbers) gives this error:

$ clang++ -std=c++14 issue.cpp -o issue
In file included from issue.cpp:1:
type_safe/include/type_safe/strong_typedef.hpp:496:62: error: invalid operands to binary expression ('const underlying_type<bitblock<type_safe::integer<unsigned long, type_safe::undefined_behavior_arithmetic> > >' (aka 'const
      type_safe::integer<unsigned long, type_safe::undefined_behavior_arithmetic>') and 'const underlying_type<bitblock<type_safe::integer<unsigned long, type_safe::undefined_behavior_arithmetic> > >')
        TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP(bitwise_and, &)
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
type_safe/include/type_safe/strong_typedef.hpp:331:30: note: expanded from macro 'TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP'
    TYPE_SAFE_DETAIL_MAKE_OP(Op, Name, StrongTypedef)                                              \
    ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
type_safe/include/type_safe/strong_typedef.hpp:203:17: note: expanded from macro 'TYPE_SAFE_DETAIL_MAKE_OP'
                Op detail::get_underlying<StrongTypedef>(static_cast<const StrongTypedef&>(rhs))); \
                ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
issue.cpp:20:15: note: in instantiation of function template specialization 'type_safe::strong_typedef_op::operator&<bitblock<type_safe::integer<unsigned long, type_safe::undefined_behavior_arithmetic> > >' requested here
  bits = bits & mask;
              ^
type_safe/include/type_safe/strong_typedef.hpp:496:9: note: candidate template ignored: could not match 'bitwise_and' against 'integer'
        TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP(bitwise_and, &)
        ^
type_safe/include/type_safe/strong_typedef.hpp:331:5: note: expanded from macro 'TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP'
    TYPE_SAFE_DETAIL_MAKE_OP(Op, Name, StrongTypedef)                                              \
    ^
type_safe/include/type_safe/strong_typedef.hpp:199:22: note: expanded from macro 'TYPE_SAFE_DETAIL_MAKE_OP'
    constexpr Result operator Op(const Name<StrongTypedef>& lhs, const Name<StrongTypedef>& rhs)   \
                     ^
1 error generated.

while if I use std::size_t instead of ts::size_t as underlying type, everything compiles fine. Am I doing something wrong, or is this some misinteraction between ts::strong_typedef and ts::integer? Or are they simply not supported when used together?

using old version of debug_assert forces compilation with -std=c++11

After adding type_safe using cmake add_subdirectory, currently used version of debug_assert does that:

if((CMAKE_CXX_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
    target_compile_options(debug_assert INTERFACE "-std=c++11")
endif()

which ends up with -std=c++11 added to compiler options (after projects own compile options).

There is a fix for this issue already in the upstream.

Any interest in arithmetical comparison of signed and unsigned integers?

It is quite common to see mixed comparisons between unsigned and signed variables spread around many code bases that I work with. The integer conversion rules of such can yield arithmetically incorrect results. Currently, type_safe seems to disable such comparisons, but it I thought it could provide either explicit functions to perform them or even allow them implicitly "under the hood" by default, provided the results are arithmetically correct.

An example pseudo code:

bool arith_lt(i32 i, u32 u) { // lt => less than
  return i < 0 || u32(i) < u;
}

What is your opinion?

Assert.hpp line 33: "Conversion from 'long' to 'unsigned int' requires a narrowing conversion."

I am a new user of type_safe, or at least I would like to be; I am using Microsoft Visual Studio 2017, though when I include seemingly any header file while set in debug configuration (occurs in both 32 and 64 bit configurations), I am met with the error stated in the title. I have included "external/debug_assert" in my project as well. The error appears on line 33 of "assert.hpp", which states the following:

inline void on_disabled_exception() noexcept
        {
            struct handler : debug_assert::set_level<1>, debug_assert::default_handler
            {
            };
            DEBUG_UNREACHABLE(handler{},
                              "attempt to throw an exception but exceptions are disabled"); //The error appears on this line.
        }

In my test project, this is the only code I have written:

#include "type_safe\integer.hpp"

void main() {}

I am unsure if I am using the library incorrectly, or if this is an error within the library itself. Any help would be appreciated, thank you.

Flag cannot be cast to bool

Unsure if the flag is intended to cast to bool or not, but the only workaround is to use (flag == true) in cases where you want to check the value of the flag.

[doc] include path specification

The readme says "You need to add include/type_safe to your include path ". However, if you would do just that and #include <boolean.hpp>, the type_safe specific includes in that file won't be found.

Probably, the documentation should read "...add include" to your include path.

type_safe::opt_(c)ref() taking references

I noted that there are no overloads for opt_(c)ref() taking references. If I understand the design behind the lib correctly, the idea is that reference forwarding and ownership must me explicit rather than implicit (So that's why a raw pointer overload is provided instead).

When working with optionals I always think of factories like opt_(c)ref() as the Just and Nothing constructors from Haskell's Maybe monad, specifically a opt_(c)ref() taking lvalue references would model just, i.e., constructing a maybe-value from a value.

What do you think?

(Early?) optimizing `constrained_type`

I've come up with a few (early?) optimization opportunities for constrained_type.

Sometimes we obtain values that are known to meet a constraint, yet if we're wrapping it in a constrained_type, we'd have to go through the verifier, increasing the overhead (I don't have measures to backup these claims). A solution for this would be implementing a constructor that doesn't verify the value.

Also, we might want to constraint a value more than an already constrained one. In such a case, it'd be convenient to have a way to specify relations between different constrained_types such that conversion from the less constrained to the more constrained doesn't verify the value.

`constrained_type`'s `Constraint` and `Verifier` requirements and effects

constrained_type's documentations specifies that

Requires: ... Constraint must be a functor of type bool(const T&) ...

However, it is unclear what else is expected of this type. In particular:

  • It is used as a base, so it can't be final.
  • Some functions require it to be
    • DefaultConstructible (this is actually on the call site)
    • MoveConstructible
    • CopyConstructible

Those same functions don't document in the Throws: what happens if an operation on a Constraint throws, nor is it accounted for in the noexcept conditions.


Looking at other definitions that use, or are intended to be used with constrained_type, in particular, null_verifier, tagged_type and owner, it seems that other requirements on Constraint (apart from those listed above) and Verifier depend on how these two interact.

To me, it feels weird that suddenly the requirements on Constraint and Verifier don't apply. Perhaps it would be a nice idea to add a note that a constrained_type with a null_verifier is treated as a special case, so that the users can rely on the actual requirements.

Clamping requires non-const reference

clamping_verifier requires a non-const reference to the value wrapped in constrained_type to clamp it, but it invoked through a const path, so its use is ill-formed.

Catch2 merged into master - With it, type_safe compilation errors on all compilers

EDIT: SOLVED. Changing test/CMakeLists.txt as shown below doesn't trigger {build}/test/catch.hpp to be replaced. I wasn't paying attention that it's an if(NOT EXISTS.... I thought I was trying non Catch2 versions, but Catch2 was remaining.

So, the fix is to become Catch2 compatible, or using the Catch1.x branch in the meantime if that's lengthy to do.


I'm extremely confused. First time trying to use type_safe, and I can't get it to compile using gcc 7.2.0, clang 5.0.0, or Visual Studio 2017 15.5 Preview 2. I'm just talking about git master, without any of my code or modifications.

Catch v2 got merged into master today.

I ran into another third party library today that doesn't work with Catch v2, so I tried if that was the issue by by changing test/CMakeLists.txt from

https://raw.githubusercontent.com/philsquared/Catch/master/single_include/catch.hpp

to

https://raw.githubusercontent.com/catchorg/Catch2/Catch1.x/single_include/catch.hpp
                                  ^^^^^^^^^^^^^^^^^^^^^^^^

But, I got the same errors.

I also tried Catch with tag v1.10.0 (Aug 26) to rule out recent Catch1.x changes as well, and got the same errors.

I also tried rolling type_safe back quite a few commits, and got the same errors.

Using Catch1.x on the other library worked, so I don't think there was any kind of crazy rebasing that screwed up the whole catch repo.

But, it looks like type_safe has been working for everyone, with the lack of issues saying otherwise, which leads me back to wondering if this is really a catch problem. Any thoughts?

P.S. I did see #47, but that's different errors in a different file, and caused by him having an outdated version of debug_assert.hpp. I didn't have a previous version of it, so it's automatically grabbing debug_assert master (5b1142e).

With clang 5.0.0:

[ 32%] Building CXX object test/CMakeFiles/type_safe_test.dir/strong_typedef.cpp.o
In file included from type_safe.git/test/strong_typedef.cpp:5:
type_safe.git/include/type_safe/strong_typedef.hpp:343:9: error: no matching function for call to
      'get_underlying'
        TYPE_SAFE_DETAIL_MAKE_OP(==, equality_comparison, bool)
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type_safe.git/include/type_safe/strong_typedef.hpp:234:22: note: expanded from macro
      'TYPE_SAFE_DETAIL_MAKE_OP'
            lhs)) Op detail::get_underlying<StrongTypedef>(detail::forward<Other>(rhs)));          \
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type_safe.git/build.clang/test/catch.hpp:1040:80: note: in instantiation of function template
      specialization 'type_safe::strong_typedef_op::operator==<type, const convert_b &, void>' requested here
    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; };
                                                                               ^
...

With gcc 7.2.0:

[ 32%] Building CXX object test/CMakeFiles/type_safe_test.dir/strong_typedef.cpp.o
In file included from type_safe.git/test/strong_typedef.cpp:5:0:
type_safe.git/include/type_safe/strong_typedef.hpp: In instantiation of ‘constexpr bool type_safe::strong_typedef_op::operator==(const type_safe::strong_typedef_op::equality_comparison<StrongTypedef>&, Other&&) [with StrongTypedef = ____C_A_T_C_H____T_E_S_T____0()::type; Other = const ____C_A_T_C_H____T_E_S_T____0()::convert_b&; <template-parameter-1-3> = void]’:
type_safe.git/build.gcc/test/catch.hpp:1040:80:   required from ‘bool Catch::compareEqual(const LhsT&, const RhsT&) [with LhsT = ____C_A_T_C_H____T_E_S_T____0()::type; RhsT = ____C_A_T_C_H____T_E_S_T____0()::convert_b]’
type_safe.git/build.gcc/test/catch.hpp:1069:63:   required from ‘const Catch::BinaryExpr<LhsT, const RhsT&> Catch::ExprLhs<LhsT>::operator==(const RhsT&) [with RhsT = ____C_A_T_C_H____T_E_S_T____0()::convert_b; LhsT = const ____C_A_T_C_H____T_E_S_T____0()::type&]’
type_safe.git/test/strong_typedef.cpp:55:9:   required from here
type_safe.git/include/type_safe/strong_typedef.hpp:234:59: error: no matching function for call to ‘get_underlying<____C_A_T_C_H____T_E_S_T____0()::type>(const ____C_A_T_C_H____T_E_S_T____0()::convert_b&)’
             lhs)) Op detail::get_underlying<StrongTypedef>(detail::forward<Other>(rhs)));          \
                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type_safe.git/include/type_safe/strong_typedef.hpp:343:9: note: in expansion of macro ‘TYPE_SAFE_DETAIL_MAKE_OP’
         TYPE_SAFE_DETAIL_MAKE_OP(==, equality_comparison, bool)
         ^~~~~~~~~~~~~~~~~~~~~~~~
...

With Visual Studio 2017 15.5 Preview 2:

type_safe.git\include\type_safe\strong_typedef.hpp(343): error C2665: 'type_safe::strong_typedef_op::detail::get_underlying': none of the 2 overloads could convert all the argument types
  type_safe.git\include\type_safe/strong_typedef.hpp(173): note: could be 'int &&type_safe::strong_typedef_op::detail::get_underlying<StrongTypedef>(StrongTypedef &&)'
          with
          [
              StrongTypedef=____C_A_T_C_H____T_E_S_T____0::type
          ]
  type_safe.git\include\type_safe/strong_typedef.hpp(166): note: or       'const int &type_safe::strong_typedef_op::detail::get_underlying<StrongTypedef>(const StrongTypedef &)'
          with
          [
              StrongTypedef=____C_A_T_C_H____T_E_S_T____0::type
          ]
  type_safe.git\include\type_safe/strong_typedef.hpp(343): note: while trying to match the argument list '(const ____C_A_T_C_H____T_E_S_T____0::convert_b)'
  build\x64-Debug\test\catch.hpp(1040): note: see reference to function template instantiation 'bool type_safe::strong_typedef_op::operator ==<____C_A_T_C_H____T_E_S_T____0::type,const RhsT&,void>(const type_safe::strong_typedef_op::equality_comparison<____C_A_T_C_H____T_E_S_T____0::type> &,Other)' being compiled
          with
          [
              RhsT=____C_A_T_C_H____T_E_S_T____0::convert_b,
              Other=const ____C_A_T_C_H____T_E_S_T____0::convert_b &
          ]
...

Future of type_safe

Thank you all for making this my most popular project with tons of daily clones!

However, I never intended that this would be such a widely used library. It started out as an experimentation ground for type safe programming techniques and then turned into my general utility library: I need an optional -> let's put it in type safe. I need a flag set -> let's put it in type safe. etc

But now it has become this huge project, so I'm not sure how to go from here.

As shown in the readme this library consists of basically three things:

  • improved integer types
  • type safe building blocks like strong typedefs or constrained type
  • vocabulary types and optional/variant

If you just want improved integer types there are better and more advanced libraries. I don't think many people actually use the type safe building blocks other than the strong typedefs facility. And the vocabulary types are there because it has become my utility library. They're useful but don't really fit the label "type safe".

So I think people come and use this library for mainly two reasons:

  1. The strong typedefs facility
  2. The vocabulary types

Please correct me if I'm wrong in that assessment. (I could be completely off the tracks there)

I'm thus proposing the following plan:

  1. Extract two other libraries out of type_safe: A strong typedefs library and a vocabulary type library.

    The strong typedefs library will be fully compatible, it is just the strong_typedefs.hpp header split into multiple files and with some more operations.

    The vocabulary type library will have some breaking changes, around optional and variant. I've learned a lot while doing them and like to revise some of my design decisions.

  2. Maybe submit the strong typedef library to Boost. It would still be usable stand alone, but it might make it easier to use in some corporations.

  3. Sometime in 2019 probably: Deprecate the stuff that has been extracted into other libraries.

This plan allows me to do three things:

  1. Keep type_safe as a library for type safe programming (only)
  2. Put part of it into Boost.
  3. Do some breaking changes to allow better vocabulary types.

Please let me know what you think of this plan.

`optional::map` doesn't work with generic lambdas which require non-const objects

Attempting to compile this code:

#include "type_safe/optional.hpp"                                                                                                                                                                                                             
                                                                                                                                                                                                                                              
int main () {                                                                                                                                                                                                                                 
  struct foo {                                                                                                                                                                                                                                
    void non_const() {}                                                                                                                                                                                                                       
  };                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                              
  type_safe::optional<foo> f = foo{};                                                                                                                                                                                                         
  f.map([](auto &&x) { x.non_const(); });                                                                                                                                                                                                     
} 

Gives the following error message on Clang 3.8:

test.cpp:9:24: error: member function 'non_const' not viable: 'this' argument has type 'const foo', but function is not marked const
  f.map([](auto &&x) { x.non_const(); });
                       ^
../type_safe/include/type_safe/detail/map_invoke.hpp:16:25: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const foo &>' requested here
            -> decltype(std::forward<Func>(f)(std::forward<Value>(v), std::forward<Args>(args)...))
                        ^
../type_safe/include/type_safe/optional.hpp:507:32: note: while substituting deduced template arguments into function template 'map_invoke' [with Func = (lambda at test.cpp:9:9), Value = const foo &, Args = <>]
            -> rebind<decltype(detail::map_invoke(std::forward<Func>(f), this->value(),
                               ^
test.cpp:9:5: note: while substituting deduced template arguments into function template 'map' [with Func = (lambda at test.cpp:9:9), Args = <>]
  f.map([](auto &&x) { x.non_const(); });
    ^
test.cpp:5:10: note: 'non_const' declared here
    void non_const() {}

And the following on GCC 5.4:

test.cpp: In instantiation of ‘main()::<lambda(auto:1&&)> [with auto:1 = const main()::foo&]’:
../type_safe/include/type_safe/detail/map_invoke.hpp:16:46:   required by substitution of ‘template<class Func, class Value, class ... Args> decltype (forward<Func>(f)(forward<Value>(v), (forward<Args>)(type_safe::detail::map_invoke::args)...)) type_safe::detail::map_invoke(Func&&, Value&&, Args&& ...) [with Func = main()::<lambda(auto:1&&)>; Value = const main()::foo&; Args = {}]’
../type_safe/include/type_safe/optional.hpp:541:31:   required by substitution of ‘template<class Func, class ... Args> type_safe::basic_optional<StoragePolicy>::rebind<decltype (type_safe::detail::map_invoke(forward<Func>(f), ((const type_safe::basic_optional<StoragePolicy>*)this)->.value(), (forward<Args>)(type_safe::basic_optional::map::args)...))> type_safe::basic_optional<StoragePolicy>::map(Func&&, Args&& ...) const && [with Func = main()::<lambda(auto:1&&)>; Args = {}]’
test.cpp:9:40:   required from here
test.cpp:9:24: error: passing ‘const main()::foo’ as ‘this’ argument discards qualifiers [-fpermissive]
   f.map([](auto &&x) { x.non_const(); });
                        ^
test.cpp:5:10: note:   in call to ‘void main()::foo::non_const()’
     void non_const() {}

Expected behaviour: Compile and run

This is because the implementation of map uses trailing return types, even in C++14 mode, which will cause the body of the lambda to be instantiated for the const-qualified overload, giving a hard error (it won't SFINAE). This could be solved by using auto return type deduction when compiling in C++14 mode.

CMake: External should use current source directory

The dependency on debug_assert in cmake is handled like this:

execute_process(COMMAND git submodule update --init -- external/debug_assert
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_subdirectory(${CMAKE_SOURCE_DIR}/external/debug_assert)

However, when adding type_safe as a git submodule to my project, I had to change CMAKE_SOURCE_DIR to CMAKE_CURRENT_SOURCE_DIR otherwise the path to external/debug_assert would not be correct. I believe this is the correct fix?

Can't compile with neither g++7 or clang++5

Cloned the repository from scratch, then mkdir build && cd build && cmake .. && make. Error:

[  2%] Building CXX object example/CMakeFiles/type_safe_example_optional.dir/optional.cpp.o
In file included from /home/vittorioromeo/Repos/ts2/example/optional.cpp:10:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/optional_ref.hpp:9:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/reference.hpp:15:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:12:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/types.hpp:17:
/home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:356:70: error: right operand to ? is void, but left operand is of type 'result_type' (aka 'long')
        return i <= Integer(std::numeric_limits<result_type>::max()) ?
                                                                     ^
/home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:370:16: note: in instantiation of function template specialization 'type_safe::make_signed<unsigned long, void>' requested here
        return make_signed(static_cast<Integer>(i));
               ^
/home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:106:40: note: in instantiation of function template specialization 'type_safe::make_signed<unsigned long, type_safe::undefined_behavior_arithmetic>'
      requested here
            get(*this) = make_unsigned(make_signed(get(*this)) + get(rhs));
                                       ^
In file included from /home/vittorioromeo/Repos/ts2/example/optional.cpp:10:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/optional_ref.hpp:9:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/reference.hpp:15:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:12:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/types.hpp:17:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:14:
/home/vittorioromeo/Repos/ts2/include/type_safe/arithmetic_policy.hpp:140:87: error: left operand to ? is void, but right operand is of type 'long'
            return detail::will_addition_error(detail::arithmetic_tag_for<T>{}, a, b) ?
                                                                                      ^
/home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:611:33: note: in instantiation of function template specialization 'type_safe::undefined_behavior_arithmetic::do_addition<long>' requested here
        return Policy::template do_addition<type>(static_cast<A>(a), static_cast<B>(b));
                                ^
/home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:106:64: note: in instantiation of function template specialization 'type_safe::operator+<long, long, type_safe::undefined_behavior_arithmetic>' requested
      here
            get(*this) = make_unsigned(make_signed(get(*this)) + get(rhs));
                                                               ^
In file included from /home/vittorioromeo/Repos/ts2/example/optional.cpp:10:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/optional_ref.hpp:9:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/reference.hpp:15:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:12:
In file included from /home/vittorioromeo/Repos/ts2/include/type_safe/types.hpp:17:
/home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:389:32: error: right operand to ? is void, but left operand is of type 'result_type' (aka 'unsigned long')
        return i >= Integer(0) ? static_cast<result_type>(i) :
                               ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/vittorioromeo/Repos/ts2/include/type_safe/integer.hpp:401:16: note: in instantiation of function template specialization 'type_safe::make_unsigned<long, void>' requested here
        return make_unsigned(static_cast<Integer>(i));
               ^
/home/vittorioromeo/Repos/ts2/include/type_safe/index.hpp:106:26: note: in instantiation of function template specialization 'type_safe::make_unsigned<long, type_safe::undefined_behavior_arithmetic>' requested
      here
            get(*this) = make_unsigned(make_signed(get(*this)) + get(rhs));
                         ^

Stateless `bounded` constraint

Have you considered having a bounded constraint which doesn't store the bounds as data members, but in the template arguments instead?

I think this is achievable for arbitrary literal types. The bound values can be encoded in types, as done in boost::hana.

The current behaviour of the bounds being dynamic could be indicated by a special value, as done with the dynamic_extent in the proposed span. This could be done with an Optional or Variant that stores the arbitrary literal type value if the bound is static.

I am not sure how feasible this is with C++11, which this library targets.

is_valid<> interpreted as non constant expressions in variant SFINAEd constructors

When building cppast with a cross building gcc 5.2 the compiler cannot resolve a call to the type_safe::variant constructor because in every overload SFINAEd with is_valid<> the is_valid instance is not evaluated as a constant expression. See error at the end of this build log.

Note the build matrix, other compilers are accepting this code with no issues.
This issue is non blocking for me, the rest of the cross compiling matrix is working and our production toolchains are working great :)

More info: The build uses manu343726/tinyrefl:gcc5-armv7 docker image, built using lasote/conangcc5-armv7 as base. Both images are available at docker hub, so you can pull them to play with the toolchain if you want.

Create release

I think this is a fantastic library. For us in the corporate world having named released with version numbers makes it much simpler to keep track of our dependencies. Would it be possible to tag a release with the current code as is? There already is an old 0.1, what about marking current HEAD as 0.2?

Inheritance composition with `strong_typedef_op`s

The change from friend to free functions of the strong_typedef_ops makes the following result in error:

struct X : type_safe::strong_typedef<X, int>,
           type_safe::strong_typedef_op::equality_comparison<X> {};
struct Y : X {};
constexpr bool _{Y{} == Y{}}; // errors

The end result is that different types are passed to the candidates, one being X and other Y, and things don't work as expected.

Extra information in LICENSE

I am packaging some of your libraries in AUR.
One of the requirements is to provide a correct license file.

However, it seems that there is more information that it should be in your license file.
Probably, you just copied from standardese.

Bitwise operators for strong_typedef

Hi
Any reason for not including bitwise operators in strong_typedef? I have included your strong_typedef in to a project I'm working on and needed the bitwise stuff. It was easy to add, but I'm a bit curious if there is a reason why its not already in? Is there a problem with that, that I haven't thought of?

Magnus

Compilation error in gcc 4.8.4

Hi

compilation fails on Ubuntu 14.04 (gcc 4.8.4)

09:34:17: Running steps for project type_safe...
09:34:17: Starting: "/usr/bin/cmake" --build . --target all
Scanning dependencies of target type_safe_example_constrained
[  5%] Building CXX object example/CMakeFiles/type_safe_example_constrained.dir/constrained.cpp.o
Linking CXX executable type_safe_example_constrained
[  5%] Built target type_safe_example_constrained
Scanning dependencies of target type_safe_example_optional
[ 11%] Building CXX object example/CMakeFiles/type_safe_example_optional.dir/optional.cpp.o
In file included from /home/davide/Software/type_safe/example/optional.cpp:9:0:
/home/davide/Software/type_safe/include/type_safe/optional.hpp: In instantiation of ‘class type_safe::basic_optional<type_safe::direct_optional_storage<char> >’:
/home/davide/Software/type_safe/example/optional.cpp:15:47:   required from here
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: error: call of overloaded ‘get_value()’ is ambiguous
         auto value() const && noexcept -> decltype(std::move(policy_).get_value())
              ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: note: candidates are:
/home/davide/Software/type_safe/include/type_safe/optional.hpp:828:32: note: const T& type_safe::direct_optional_storage<T>::get_value() const & [with T = char; type_safe::direct_optional_storage<T>::const_lvalue_reference = const char&]
         const_lvalue_reference get_value() const& noexcept
                                ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:842:32: note: const T&& type_safe::direct_optional_storage<T>::get_value() const && [with T = char; type_safe::direct_optional_storage<T>::const_rvalue_reference = const char&&]
         const_rvalue_reference get_value() const&& noexcept
                                ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp: In instantiation of ‘class type_safe::basic_optional<type_safe::direct_optional_storage<int> >’:
/home/davide/Software/type_safe/example/optional.cpp:21:32:   required from here
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: error: call of overloaded ‘get_value()’ is ambiguous
         auto value() const && noexcept -> decltype(std::move(policy_).get_value())
              ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: note: candidates are:
/home/davide/Software/type_safe/include/type_safe/optional.hpp:828:32: note: const T& type_safe::direct_optional_storage<T>::get_value() const & [with T = int; type_safe::direct_optional_storage<T>::const_lvalue_reference = const int&]
         const_lvalue_reference get_value() const& noexcept
                                ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:842:32: note: const T&& type_safe::direct_optional_storage<T>::get_value() const && [with T = int; type_safe::direct_optional_storage<T>::const_rvalue_reference = const int&&]
         const_rvalue_reference get_value() const&& noexcept
                                ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp: In instantiation of ‘class type_safe::basic_optional<type_safe::direct_optional_storage<type_safe::basic_optional<type_safe::direct_optional_storage<int> > > >’:
/home/davide/Software/type_safe/include/type_safe/optional.hpp:83:97:   required by substitution of ‘template<class Optional> using unwrap_optional_t = typename std::decay<typename std::conditional<type_safe::detail::is_optional_impl<typename std::decay<typename std::decay<_Tp>::type::value_type>::type>::value, typename std::decay<_Tp>::type::value_type, typename std::decay<_Tp>::type>::type>::type [with Optional = type_safe::basic_optional<type_safe::direct_optional_storage<char> >::rebind<type_safe::basic_optional<type_safe::direct_optional_storage<int> > >]’
/home/davide/Software/type_safe/include/type_safe/optional.hpp:438:14:   required by substitution of ‘template<class Func> type_safe::detail::unwrap_optional_t<decltype (this->.map(forward<Func>(f)))> type_safe::basic_optional<StoragePolicy>::bind(Func&&) && [with Func = Func; StoragePolicy = type_safe::direct_optional_storage<char>] [with Func = type_safe::basic_optional<type_safe::direct_optional_storage<int> > (&)(char)]’
/home/davide/Software/type_safe/example/optional.cpp:55:21:   required from here
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: error: call of overloaded ‘get_value()’ is ambiguous
         auto value() const && noexcept -> decltype(std::move(policy_).get_value())
              ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:350:14: note: candidates are:
/home/davide/Software/type_safe/include/type_safe/optional.hpp:828:32: note: const T& type_safe::direct_optional_storage<T>::get_value() const & [with T = type_safe::basic_optional<type_safe::direct_optional_storage<int> >; type_safe::direct_optional_storage<T>::const_lvalue_reference = const type_safe::basic_optional<type_safe::direct_optional_storage<int> >&]
         const_lvalue_reference get_value() const& noexcept
                                ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:842:32: note: const T&& type_safe::direct_optional_storage<T>::get_value() const && [with T = type_safe::basic_optional<type_safe::direct_optional_storage<int> >; type_safe::direct_optional_storage<T>::const_rvalue_reference = const type_safe::basic_optional<type_safe::direct_optional_storage<int> >&&]
         const_rvalue_reference get_value() const&& noexcept
                                ^
/home/davide/Software/type_safe/example/optional.cpp: In function ‘int task_monadic(const string&)’:
/home/davide/Software/type_safe/example/optional.cpp:57:10: error: request for member ‘value_or’ in ‘type_safe::basic_optional<StoragePolicy>::map(Func&&) && [with Func = task_monadic(const string&)::__lambda4; StoragePolicy = type_safe::direct_optional_storage<char>; type_safe::basic_optional<StoragePolicy>::rebind<decltype (forward<Func>(f)(std::move(this->.value())))> = type_safe::basic_optional<type_safe::direct_optional_storage<char> >; typename StoragePolicy::rebind<decltype (forward<Func>(f)(std::move(this->.value())))> = type_safe::direct_optional_storage<char>]((* &<lambda closure object> task_monadic(const string&)::__lambda4{})).type_safe::basic_optional<StoragePolicy>::bind<type_safe::basic_optional<type_safe::direct_optional_storage<int> > (&)(char)>((* lookup))’, which is of non-class type ‘::’
         .value_or(0);
          ^
/home/davide/Software/type_safe/include/type_safe/optional.hpp:438: confused by earlier errors, bailing out
Preprocessed source stored into /tmp/cchCeNth.out file, please attach this to your bugreport.
make[2]: *** [example/CMakeFiles/type_safe_example_optional.dir/optional.cpp.o] Error 1
make[1]: *** [example/CMakeFiles/type_safe_example_optional.dir/all] Error 2
make: *** [all] Error 2
09:34:18: The process "/usr/bin/cmake" exited with code 2.
Error while building/deploying project type_safe (kit: Desktop)
When executing step "Make"
09:34:18: Elapsed time: 00:01.

Overly restrictive conversion between integer instantiations

Hi Jonathan!

Looking at your documentation for type_safe and saw this about integer:

A conversion is considered safe if both integer types have the same signedness and the size of the value being converted is less than or equal to the destination size.

But what if you are converting, say, from uint16_t to int32_t? Is this not allowed? Is it not safe?

BTW, I have a similar test with no such restriction.

Cheers,
John

Type wrappers do not have a std::hash specialization defined

When attempting to use type_safe's abstract types as a key in an unordered map, I am notified by the compiler (I am using MSVC) that hashes are not provided for the types. As a generic type wrapper library I would expect std::hash specializations to be provided for all types.

Example code:

#include <unordered_map>
#include "type_safe\integer.hpp"

void main() {
	std::unordered_map<type_safe::integer<int32_t>, int /* arbitrary test value */> map;
}

type_safe::boolean with std::set

Hello,

I am unable to use strong_typedefs with std::set because the STL cannot convert type_safe::boolean to a bool value.

Here is an example of what I am trying to do:

struct thread_id :
  type_safe::strong_typedef<thread_id, type_safe::integer<uint32_t>>,
  type_safe::strong_typedef_op::equality_comparison<thread_id>,
  type_safe::strong_typedef_op::relational_comparison<thread_id>,
  type_safe::strong_typedef_op::output_operator<thread_id>
{
  using strong_typedef::strong_typedef;
};

int main(int argc, char ** argv)
{
  std::set<thread_id> subscribers;
  thread_id subscriber(0u);

  subscribers.insert(subscriber); // error here

  return 0;
}

And here is the error:

/usr/include/c++/5/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = thread_id]’:
/usr/include/c++/5/bits/stl_tree.h:1810:11: required from ‘std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Key = thread_id; _Val = thread_id; _KeyOfValue = std::_Identity<thread_id>; _Compare = std::less<thread_id>; _Alloc = std::allocator<thread_id>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = thread_id]’
/usr/include/c++/5/bits/stl_tree.h:1863:28: required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = const thread_id&; _Key = thread_id; _Val = thread_id; _KeyOfValue = std::_Identity<thread_id>; _Compare = std::less<thread_id>; _Alloc = std::allocator<thread_id>]’
/usr/include/c++/5/bits/stl_set.h:485:29: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const value_type&) [with _Key = thread_id; _Compare = std::less<thread_id>; _Alloc = std::allocator<thread_id>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<thread_id>; std::set<_Key, _Compare, _Alloc>::value_type = thread_id]’
/home/badrmari/Projects/phd/sim-sync/src/sim-sync-cl/main.cpp:21:32: required from here
/usr/include/c++/5/bits/stl_function.h:387:22: error: cannot convert ‘type_safe::boolean’ to ‘bool’ in return
{ return __x < __y; }

Is there a way to fix this with an additional strong_typedef_op? Or is strong_typedef incompatible with STL containers that need to do comparisons?

(Using gcc 5.4.0)

strong_typedef of user-defined types with non-constexpr operators

Using the provided type_safe::strong_typedef_op mixins with a user-defined type with non-constexpr operators fails in Visual C++ and gcc. It appears to work in clang, which is interesting.

Visual C++
clang

Since the mixins are provided as convenience, I can create non-constexpr mixins as needed. I'm not sure how you'd want to address this, if at all, in the lib.

User-defined literals don't perform overflow checks

One would expect that the following snippet would result in some kind of error, compile-time or otherwise:

#include <type_safe/types.hpp>

int main() {
  using namespace type_safe::literals;
  return (1234_u8).get();
}

The culprit is easy to spot---static_cast is used to convert unsigned long long into the target type, without any range verification:

constexpr uint8_t operator"" _u8(unsigned long long val) {
  return uint8_t(static_cast<std::uint8_t>(val));
}

A simple solution would be to have something like return (val > std::numeric_limits<std::uint8_t>::max()) ? throw std::out_of_range(__func__) : uint8_t(static_cast<std::uint8_t>(val));, but this won't fail at compile-time except in some constexpr-using cases.

Using the template form template<char...> operator"" _u8() would permit using static_assert, but this is a more involved solution that requires parsing the literal manually.

cast for access to value ref considered problematic

When you need to add functionality to a strong_typedef it is a bit cumbersome to need to implement it in terms of static_cast<type&>(*this).

  struct name : ts::strong_typedef<name, std::string> {
    using strong_typedef::strong_typedef;
    auto length() const { return static_cast<std::string const&>(*this).length(); }
  };

It works, but it's a hurdle which I fear works against the possibility to introduce strong_typedef to less skilled developers.

I'm not sure what's a good way to do that, though.

Making the member value_ protected instead would work, but I don't really like it.

Another option is to use protected inheritance of the type (when possible), but that is also quite ugly.

Yet an option is to add some access function that essentially does the static_cast<>, but that pollutes the name space.

I guess there are better ways.

Just wanted to raise the usability concern.

Arithmetic type wrappers are not PODs

I noticed that arithmetic type wrappers are not PODs. This (among other things) forbids standard library from memmoveing arrays and vectors of them when using std::copy.
This could be solved by explicitly deleting default constructor from class templates type_safe::integer and type_safe::floating_point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.