All Projects → doom → strong_type

doom / strong_type

Licence: MIT license
C++ implementation of strong types

Programming Languages

C++
36643 projects - #6 most used programming language
CMake
9771 projects

Projects that are alternatives of or similar to strong type

SparForte
Bourne shell, template engine, scripting language mission-critical, scalable projects. Based a ISO standard proven effective for large, mission-critical projects, SparForte is designed for fast development while, at the same time, providing easier designing, maintenance and bug removal. About 120.000 lines of code.
Stars: ✭ 47 (+2.17%)
Mutual labels:  strongly-typed
graphql-ts-client
Typescript DSL for GraphQL.
Stars: ✭ 124 (+169.57%)
Mutual labels:  strongly-typed
WebsocketPromisify
Makes websocket's API just like REST with Promise-like API, with native Promises.
Stars: ✭ 18 (-60.87%)
Mutual labels:  strongly-typed
Grakn
TypeDB: a strongly-typed database
Stars: ✭ 2,947 (+6306.52%)
Mutual labels:  strongly-typed
hitchstory
Type-safe, StrictYAML based BDD framework for python.
Stars: ✭ 24 (-47.83%)
Mutual labels:  strongly-typed
typeql
TypeQL: the query language of TypeDB - a strongly-typed database
Stars: ✭ 157 (+241.3%)
Mutual labels:  strongly-typed
nest-typed-config
Intuitive, type-safe configuration module for Nest framework ✨
Stars: ✭ 47 (+2.17%)
Mutual labels:  strongly-typed
linq-collections
Strongly typed Linq and Collections implementation for Javascript and TypeScript (ECMAScript 5)
Stars: ✭ 112 (+143.48%)
Mutual labels:  strongly-typed
forms-typed
Want types in your forms? Want to have nested forms? This is the place to be...
Stars: ✭ 79 (+71.74%)
Mutual labels:  strongly-typed
type-lite
type - Strong types for C++98, C++11 and later in a single-file header-only library
Stars: ✭ 25 (-45.65%)
Mutual labels:  strongly-typed
typedorm
Strongly typed ORM for DynamoDB - Built with the single-table-design pattern in mind.
Stars: ✭ 224 (+386.96%)
Mutual labels:  strongly-typed
typedb
TypeDB: a strongly-typed database
Stars: ✭ 3,152 (+6752.17%)
Mutual labels:  strongly-typed
bhl
bhl is a strictly typed programming language specifically tailored for gameplay logic scripting.
Stars: ✭ 20 (-56.52%)
Mutual labels:  strongly-typed

strong_type

C++ implementation of strong types

Build Status
Linux (gcc-8, clang-8) / OSX Build Status

Table of contents

What is this ?

This tiny library provides a way to wrap an existing type in order to give it additional meaning. If you are already familiar with the concept of strong types, you can skip directly to the Examples section.

Otherwise, read along !

A tour of the library

A minimal example

Let's take a basic example: we want to represent a distance in our code.

The immediate idea we could have would be to use an integral type, such as int:

int distance_from_a_to_b = 10;

However, the type of the variable we work with does not convey any information about what its value actually represents. The only thing it tells us is how it is implemented. As a programmer reading the above code, you would need to rely on the name of the variable in order to understand the code.

Adding expressiveness

This can be easily fixed using a type alias:

using distance = int;
distance from_a_to_b = 10;

That way, our code is more expressive, and easier to read for humans.

But we still have an issue ! From the compiler's point of view, the int and distance types are identical. This can lead to error-prone constructs:

int definitely_not_a_distance = 10;
distance from_a_to_b = definitely_not_a_distance; // Ouch !

The code above is not correct, because it allows converting definitely_not_a_distance (which is clearly, not a distance) to a distance object implicitly.

This is the first case for which this library can help: it can "hide" the real nature of a type, in order to prevent errors and unwanted conversions.

Adding strong typing

In order to fully hide the implementation of a type, we use the st::type wrapper. It takes two (or more, but wait !) template parameters : the type to wrap, and a tag to guarantee its uniqueness.

using distance = st::type<int, struct distance_tag>;

Note: the tag can be any type, as long as it is only used as tag by a single strong type

Now, both the programmer and the compiler can distinguish a distance from a regular int.

int definitely_not_a_distance = 10;
int a_distance_value = 10;
// distance from_a_to_b = definitely_not_a_distance; // Not OK, would not compile
distance from_a_to_b = distance(a_distance); // OK
distance copy = from_a_to_b; // OK

As shown below, it is also possible to access the internal value of the strong type:

auto distance_value = from_a_to_b.value();

Customizing behavior

Now that we created a new type that hides its underlying implementation, we also lost access to the operations supported by the underlying type.

Why is that so ? Well, in our case, the concept represented by distance might not support all the operations allowed by the int type. For example, while you can add two distances together to make a longer distance, you clearly cannot multiply a distance with another distance. However, you can multiply a distance with a regular number, in order to scale it.

In order to customize the behavior of our strong types, this library uses the concept of traits. Traits are features that can be added to a type in order to give it additional behavior. Some basic traits are provided directly by the library (see the Built-In traits section), but it is also possible to write your own.

A strong type can use traits like below:

using distance = st::type<int, struct distance_tag,
	st::addable, // distances can be added together
	st::multiplicable_with<int> // distances can be scaled by a given factor
>;

Examples

This library provides two different ways to define strong types, each with different levels of complexity and flexibility.

The easy way

This is the preferred way to create a basic strong type. It requires a type tag in order to guarantee the strength of the using alias. Custom behavior can only be added through traits.

using integer = st::type<
    int,
    struct integer_tag,
    st::arithmetic,
    st::addable_with<int>
>;

The customizable way

This way makes it easier to customize a strong type because it skips the st::type intermediate. Therefore, it requires creating a structure manually, which also allows defining custom member functions without having to use traits. However, traits are still available through inheritance.

struct int_with_a_member :
    public st::type_base<int>,
    public st::traits::arithmetic<int_with_a_member>
{
    using st::type_base<int>::type_base;

    constexpr bool is_zero() const noexcept
    {
        return value() == 0;
    }
};

Built-In traits

The table below describes the built-in traits that can be applied to a given strong type T. Unless specified otherwise, these traits just forward the requested operation to the underlying types.

Trait Behavior
addable Two T objects can be added to obtain a new T.
addable_with<U> A T object can be added with a U object to obtain a new T.
subtractable A T object can be subtracted from another T object to obtain a new T
subtractable_to<U> A T object can be subtracted from another T object to obtain a new U.
multiplicable Two T objects can be multiplied to obtain a new T.
multiplicable_with<U> A T object can be multiplied with a U object to obtain a new T.
dividable A T object can be divided by another T object to obtain a new T.
dividable_by<U> A T object can be divided by a U object to obtain a new T.
dividable_to<U> A T object can be divided by another T object to obtain a new U.
modulable A T object can be moduled from another T object to obtain a new T.
incrementable A T object can be pre-incremented and post-incremented.
decrementable A T object can be pre-decremented and post-decremented.
equality_comparable Two T objects can be compared for equality (supports == and !=).
orderable Two T objects can be ordered (supports <, >, <=, >=).
arithmetic Shorthand trait for addable, subtractable, multiplicable, dividable, modulable, incrementable, decrementable, equality_comparable and orderable.
bitwise_orable Two T objects can be bitwise OR-ed to obtain a new T.
bitwise_orable_with<U> A T object can be bitwise OR-ed with a U object to obtain a new T.
bitwise_andable Two T objects can be bitwise AND-ed to obtain a new T.
bitwise_andable_with<U> A T object can be bitwise AND-ed with a U object to obtain a new T.
bitwise_xorable Two T objects can be bitwise XOR-ed to obtain a new T.
bitwise_xorable_with<U> A T object can be bitwise XOR-ed with a U object to obtain a new T.
bitwise_negatable A T object can be bitwise negated (NOT) to obtain a new T.
bitwise_manipulable Shorthand trait for bitwise_orable,bitwise_orable_with, bitwise_andable, bitwise_andable_with, bitwise_xorable, bitwise_xorable_with, bitwise_negatable and bitwise_manipulable.
hashable A T object can be hashed using std::hash (provided that its underlying type can be hashed using std::hash).
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].