Giter Club home page Giter Club logo

namedtype's Introduction

Build Status GitHub

A strong type is a type used in place of another type to carry specific meaning through its name.

This project experiments with strong types in C++. All components are in the namespace fluent. You can find a collection of blog posts explaining the rationale of the library and usages for strong types on Fluent C++.

become a patron

Basic usage

It central piece is the templated class NamedType, which can be used to declare a strong type with a typedef-like syntax:

using Width = NamedType<double, struct WidthTag>;
using Height = NamedType<double, struct HeightTag>;

which can be used to make interfaces more expressive and more robust. Note how the below constructor shows in which order it expects its parameters:

class Rectangle
{
public:
    Rectangle(Width width, Height height) : width_(width.get()), height_(height.get()) {}
    double getWidth() const { return width_; }
    double getHeight() const { return height_; }

private:
    double width_;
    double height_;
};

Strong types are about better expressing your intentions, both to the compiler and to other human developers.

Strong typing over generic types

This implementation of strong types can be used to add strong typing over generic or unknown types such as lambdas:

template<typename Function>
using Comparator = NamedType<Function, struct ComparatorTag>;

template <typename Function>
void performAction(Comparator<Function> comp)
{
    comp.get()();
}

performAction(make_named<Comparator>([](){ std::cout << "compare\n"; }));

Strong typing over references

The NamedType class is designed so that the following usage:

using FamilyNameRef = NamedType<std:string&, struct FamilyNameRefTag>;

behaves like a reference on an std::string, strongly typed.

Inheriting the underlying type functionalities

You can declare which functionalities should be inherited from the underlying type. So far, only basic operators are taken into account. For instance, to inherit from operator+ and operator<<, you can declare the strong type:

using Meter = NamedType<double, MeterTag, Addable, Printable>

There is one special skill, FunctionCallable, that lets the strong type be converted in the underlying type. This has the effect of removing the need to call .get() to get the underlying value. And MethodCallable enables operator-> on the strong type to invoke methods on the underlying type.

The skill Callable is the union of FunctionCallable and MethodCallable.

Named arguments

By their nature strong types can play the role of named parameters:

using FirstName = NamedType<std::string, struct FirstNameTag>;
using LastName = NamedType<std::string, struct LastNameTag>;

void displayName(FirstName const& theFirstName, LastName const& theLastName);

// Call site
displayName(FirstName("John"), LastName("Doe"));

But the nested type argument allows to emulate a named argument syntax:

using FirstName = NamedType<std::string, struct FirstNameTag>;
using LastName = NamedType<std::string, struct LastNameTag>;

static const FirstName::argument firstName;
static const LastName::argument lastName;

void displayName(FirstName const& theFirstName, LastName const& theLastName);

// Call site
displayName(firstName = "John", lastName = "Doe");

You can have a look at tests.cpp for usage examples.

become a patron

namedtype's People

Contributors

ams21 avatar anstow avatar arvidn avatar bebuch avatar benhetherington avatar emmenlau avatar idragnev avatar joboccara avatar jonburla avatar joris-planorama avatar juliancarrivick avatar knatten avatar mropert avatar oysteinbrandt avatar pemessier avatar shachlan avatar stanley00 avatar travnick avatar vincentzalzal 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

namedtype's Issues

The skills for ++ / -- don't compile

I'm using MSVC, and it complains it can't convert the underlying type to the named type. Looking at the definition, I see it really is doing that, which seems wrong.

Compilation error on g++ 7.3.0

sl@acer17:~/project/NamedType$
make
g++ -std=c++14 -g -O0 main.cpp -o main -Wall -Wextra -Wno-noexcept-type
main.cpp: In function ‘void changeValue(NameRef)’:
main.cpp:51:18: error: passing ‘std::remove_reference_t<std::__cxx11::basic_string<char>&> {aka const std::__cxx11::basic_string<char>}’ as ‘this’ argument discards qualifiers [-fpermissive]
     name.get() = "value2";
                  ^~~~~~~~
In file included from /usr/include/c++/7/string:52:0,
                 from catch.hpp:207,
                 from main.cpp:2:
/usr/include/c++/7/bits/basic_string.h:693:7: note:   in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
       operator=(const _CharT* __s)
       ^~~~~~~~
In file included from named_type.hpp:5:0,
                 from main.cpp:10:
underlying_functionalities.hpp: In instantiation of ‘struct fluent::MethodCallable<fluent::NamedType<____C_A_T_C_H____T_E_S_T____32()::A&, ____C_A_T_C_H____T_E_S_T____32()::StrongATag, fluent::Callable> >’:
underlying_functionalities.hpp:120:8:   required from ‘struct fluent::Callable<fluent::NamedType<____C_A_T_C_H____T_E_S_T____32()::A&, ____C_A_T_C_H____T_E_S_T____32()::StrongATag, fluent::Callable> >’
named_type_impl.hpp:21:19:   required from ‘class fluent::NamedType<____C_A_T_C_H____T_E_S_T____32()::A&, ____C_A_T_C_H____T_E_S_T____32()::StrongATag, fluent::Callable>’
main.cpp:283:5:   required from here
underlying_functionalities.hpp:115:14: error: forming pointer to reference type ‘____C_A_T_C_H____T_E_S_T____32()::A&’
     T const* operator->() const { return std::addressof(this->underlying().get()); }
              ^~~~~~~~
underlying_functionalities.hpp:116:8: error: forming pointer to reference type ‘____C_A_T_C_H____T_E_S_T____32()::A&’
     T* operator->() { return std::addressof(this->underlying().get()); }
        ^~~~~~~~
Makefile:4: recipe for target 'main' failed
make: *** [main] Error 1
sl@acer17:~/project/NamedType$

sl@acer17:~/project/NamedType$ g++ --version
g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Add Ability to Disable Default-Constructor

Somewhat contrary to #14, for my situation, I'd potentially like to disable default-construction, ideally by including a skill.

I think default-construction makes a lot of sense in many situations. And also, non default-constructible types can cause issue with some classes - a particular container may require this, for example.

But to think in a wider context, where we use our named types is to ensure that we don't have nonsensical values entering our code. We generally want to restrict places where implicit conversions are happening, and be explicit instead. Default-construction would be one place this is implicit.

I am using some NamedTypes to wrap things like ID numbers between opaque structures, frame numbers in a video, various types that effectively work like foreign keys.
A default constructor for an underlying type makes some sense - an empty string would be a good starting point, and if I'm going to have a numerical value, starting it at zero also makes sense. But to have a default-constructed ID doesn't make much sense in my applicaion - that ID has to come from somewhere, being correlated with a separate entity. A frame number has an implicit meaning in respect to the timestamps in a video frame. Just starting it at zero when we have a new one sort-of violates the principles of strong typing in the first place. Thus, a default-constructor, even if only temporarily, effectively forms a relationship that doesn't really exist, by forming a correlation between the element with ID 0, and the current scope where we created it.

The place I think I've seen this in my code is using std::map::operator[]. Unlike calling at(), operator[] will insert a default-constructed value when the given key doesn't exist. This makes a lot of sense in some situations, but I think for others, it may be a disadvantage. Whether or not this turns out to be a bug in my application, if we have a map betwee two different IDs/keys, inserting a default value when the (map's) key isn't found then violates the point of the mapping in the first place. This is an issue in my code, I should probably correct it to call at() and catch an exception instead, however, in this case, disabling the default-constructor should prevent the code from compiling inside map.

I'd therefore like to suggest that DefaultConstructible be made an inheritable skill, in the same manner as other skills, and perhaps one that could be turned off, by adding somehting like a NotDefaultConstructible pseudo-skill that would allow disabling this skill - otherwise, it may be that skills like Arithmetic would then need to be broken apart and specified as a long list, just to disable the DefaultConstructible.

Thanks :)

sizeof(Meter) != sizeof(double) with Visual Studio 2019

Thanks for the excellent library! I've added NamedType to our CI and enabled the tests, but the tests fail with Visual Studio 2019 16.7.7 (current latest). I've tested with both the MSVC compiler and the supplied ClangCl. The error I get is:

1: C:\gitlabci\NamedType\test\tests.cpp(504): FAILED:
1:   REQUIRE( sizeof(Meter) == sizeof(double) )
1: with expansion:
1:   16 == 8
1: 
1: ===============================================================================
1: test cases: 37 | 36 passed | 1 failed
1: assertions: 79 | 78 passed | 1 failed

I've checked the code and as far as I can see FLUENT_EBCO is correctly defined as __declspec(empty_bases). I'm not sure why the sizes may be wrong, then. Any help would be appreciated!

How about a new release?

Hello,

it's been more than two years since v1.1.0 got released. I'd like to add a NamedType recipe to vcpkg and would appreciate a recent release for that.

What do you think?

Thanks,
Gregor

Strongly typed indices in arrays

Is there a solution to make an array with strong indexing? Such as:

using FooIndex = NamedType<size_t, struct FooTag>;
using BarIndex = NamedType<size_t, struct BarTag>;

std::vector<Foo, FooIndex> foos;
std::vector<Bar, BarIndex> bars;
...
BardIndex barIndex = ..;
FooIndex fooIndex = ...;
foos[barIndex] = Foo(); //should not compile
foos[fooIndex] = Foo(); //ok

Theoretically it's often fine to just deduce FooIndex = NamedType<size_t, Foo> for such containers. Is there a way to achieve it in an stl-compatible way?

<iostream> still included in embedded systems

I know the issue has already been reported in issue #57 and the following code:

#if defined(__STDC_HOSTED__)
#    define FLUENT_HOSTED 1
#else
#    define FLUENT_HOSTED 0
#endif

was added to disable the inclusion of <iostream> for freestanding compilers.
Unfortunately, for the STM32 microcontrollers at least, __STDC_HOSTED__ is defined and is equal to 1, so <iostream> is still included.
Setting the -ffreestanding flag is of little use as this has many more undesired collateral effects beyond setting __STDC_HOSTED__ to 0.
The footprint of <iostream> is around 150 kB, which is an enormous amount in my embedded world.

Would it be possible to add a user defined macro that (in conjunction with the __STDC_HOSTED__ macro) enables or disables the inclusion of the <iostream> library?

Thanks for your attention.

better operator<< with std::basic_ostream

`template
struct Printable : crtp<T, Printable>
{
template
void print(std::basic_ostream& os) const { os << this->underlying().get(); }
};

template <typename CharT, typename T, typename Parameter, template class... Skills>
std::basic_ostream& operator<<(std::basic_ostream& os, NamedType<T, Parameter, Skills...> const& object)
{
object.print(os);
return os;
}`

cereal serialization functions

It took me a little while to figure out how to best serialize a NamedType using cereal (which is the BEST C++ serialization library I have found - if someone knows of a better one, please let me know). I am not sure of a good way to add this to the fluent codebase, so I thought I would post this issue in case this is helpful to others.

Cereal Serialization using external minimal serialization functions as described in Serialization Functions documentation.

namespace fluent {
template <typename Archive,
          typename ValueType,
          typename TagType,
          template <typename>
          class... Skills>
void load_minimal(
    Archive&,
    NamedType<ValueType, TagType, Skills...>& nobj,
    typename NamedType<ValueType, TagType, Skills...>::UnderlyingType const& val)
{
    using Ntype = NamedType<ValueType, TagType, Skills...>;
    nobj = Ntype{val};
}

template <typename Archive,
          typename ValueType,
          typename TagType,
          template <typename> class... Skills>
auto save_minimal(Archive&,
                  const NamedType<ValueType, TagType, Skills...>& nobj)
{
    return nobj.get();
}
}  // namespace fluent

Error "expression did not evaluate to a constant" with latest MSVC 19.8.5

With the latest master branch and latest MSVC 19.8.5, I get an error in the build:

C:\gitlabci\local\builds\U8oUWsz8\0\BioDataAnalysis\NamedType\test\tests.cpp(528,29): error C2131: expression did not evaluate to a constant
    static_assert((10_meter == 10_meter), "Comparable is not constexpr");
                            ^
C:\gitlabci\local\builds\U8oUWsz8\0\BioDataAnalysis\NamedType\include\NamedType\named_type_impl.hpp(83,16): note: failure was caused by attempting to access a member on an object of dynamic type 'fluent::Comparable<fluent::NamedType<unsigned __int64,MeterParameter,fluent::Addable,fluent::Comparable>>' in which the member is not defined
        return value_;
               ^
C:\gitlabci\local\builds\U8oUWsz8\0\BioDataAnalysis\NamedType\include\NamedType\named_type_impl.hpp(83,16): note: see usage of 'fluent::NamedType<unsigned __int64,MeterParameter,fluent::Addable,fluent::Comparable>::value_'
        return value_;

I think this test is originating from commit 1841331 "Adding constexpr for skill Comparable".

I have set a preprocessor define to enable explicit -std:c++17, but that should be ok, or not?

tuple of NamedTypes

Any ideas on how one might provide a custom get function to avoid extra call to get on the named type in the following:

using Type1 = NamedType<int, TypeTag>;
using Type2 = NamedType<double, TypeTag>;
auto t = make_tuple(Type(1), Type(2.0));
auto e = get<1>(t).get();

I would prefer the following:

auto e = get<1>(t);

That way I can use the same functionality regardless of whether or not the tuple holds a named type.

License is missing

  1. Add a file LICENSE containing your license text.
  2. Mention your license within your README.md.
  3. Append a short license banner on top of each of your source code file.

Provide a different license for your code source and documentation.
Your code source could be licensed using MIT or GPL,
and your documentation using CC-BY-SA.

Thanks for your talk at C++FRUG Paris ;)

Extending with boost hash to enable pair<NamedType, NamedType> as map key.

I have lots of maps with pairs as keys.
Here is an example of using boost::hash to use pairs of NamedTypes as map keys if you think it's interesting enough to add to the documentation.

--------- named_type_hash.hpp ---------

#ifndef NAMED_TYPE_HASH_HPP
#define NAMED_TYPE_HASH_HPP

#include <type_traits>
#include <boost/container_hash/hash.hpp>
#include <named_type.hpp>

namespace fluent{

template <typename T, typename Parameter, template<typename> class... Skills>
std::enable_if_t<NamedType<T, Parameter, Skills...>::is_hashable, size_t>
hash_value(const NamedType<T, Parameter, Skills...>& x) {
  boost::hash<T> hasher;
  return hasher(x.get());
}

} // namespace fluent

#endif /* NAMED_TYPE_HASH_HPP */


--------- main.cpp ---------
#include <unordered_map>
#include <iostream>
#include <utility>
#include <named_type.hpp>
#include "named_type_hash.hpp"


int main(int, char**)
{
  using session_id = fluent::NamedType<int, struct SessionIdTag, fluent::Hashable, fluent::Comparable>;
  using order_id = fluent::NamedType<int, struct OrderIdTag, fluent::Hashable, fluent::Comparable>;
  using key = std::pair<session_id, order_id>;
  using map = std::unordered_map<key, std::string, boost::hash<key>>;

  map x;
  session_id a{1};
  order_id b{2};
  key k{a, b};

  x[k] = "test";

  if (auto it = x.find({session_id{1}, order_id{2}}); it != x.end()) { 
    std::cout << it->second << "\n";
  } else { 
    std::cout << "not found\n";
  } 

  return 0;
}

Failure to perform standard compile time checks

Arithmetic NamedType does not pass std::is_arithmetic assertion. i.e.

using MyInt = fluent::NamedType<int, struct _MyInt, fluent::Arithmetic>;
static_assert(std::is_arithmetic<MyInt>() == true); // does not compile

This could be potentially fixed using the same pattern as fluent::Hashable, but need to consider other types of assertions for completeness sake

Dealing with non-copyable and non-movable types

I have a use case where a microcontroller library has non-copyable type for timers. Unfortunately, the library has not created a move constructor for this type either. Several higher level I/O types contain this timer type and thus inherit this non-copyable nature. I would like to use NamedType with some of these I/O types to distinguish different signals in my program.

I have a minimal example of the on godbolt

Is there any way to use NamedType with these non-copyable types?
Is there an equivalent to emplace / inplace construction?
Is there any way I can explicitly pass the underlying construction parameters and let NamedType construct the base type from these copyable construction parameters?

Possible to name this "named_type" instead?

Hi!

Love the library and the tricks.

Do you think it would be possible to name it as "named_type" instead?

#include "named_type/named_type.hpp" instead of #include "NamedType/named_type.hpp" ?

Organize the project

@joboccara Thanks for the great article and the implementation!

It would be nice to have all (useful) PR merged, and all issues resolved.
Additionally, I'm about to create a NuGet package, and maybe another for Arch Linux.

To achieve that, some versioning and releases are somehow required.
As far as I can see there is no activity since March 2019, so I suggest that the "main repository" should be moved to the another (more active) fork.

@AMS21 I'm not sure if the https://github.com/AMS21/NamedType is the right one.

The whole message is here to organize others contributors, so it will be possible to merge all the valuable work into one repository and release one consistent version.

Best regards!

use "final" if available

I might be so bold, as to suggest this...

   #ifdef __cpp_lib_is_final
        #define FLUENT_FINAL final
 #else
       #define FLUENT_FINAL
 #endif

Little optimization and big, clear message from interface author:

         template <
                       typename T, 
                       typename Parameter, 
                       template<typename> class... Skills
                   >
      class FLUENT_EBCO NamedType FLUENT_FINAL
            : public Skills<NamedType<T, Parameter, Skills...>>...
       {
       } ;

Regards ...

Concepts for Skills should be Separated from the NamedType

There's a nice set of concepts/traits for the skills in underlying_functionalities.hpp that would be useful for general-purpose code, including where concepts support isn't present. It would be nice to separate these out (poissibly even into a separate mini-library) from the NamedType aspect of the code, such that they can be re-used. I think also the skills would be easier to read if the concepts aspect were separated out from the NamedType-specific bits (i.e. frequent calls to .get()). I may have a go at this myself, be it as a separate small library or just integrated back here, I'll keep you posted if I do get the time.

compilers supported

Hi

What compilers are supported? I have to tried compile tests with gcc 8.1.0 but does fails.

/Greg

Markdown formatting in README.md

Insert empty line before code block begin ```cpp
Currently 2x in the file, last two ones.
Issue is that KDE universal document viewer app Okular won't recognize these code blocks correctly.

Probably empty line should always follow code block end tag ``` as well. This does not make a difference for Okular's rendering though.

can't move StrongType with MethodCallable skill.

A small piece of code which doesn't compile due to an error:

'operator->' declared as a pointer to a reference of type 'std::basic_string<char, std::char_traits<char>, std::allocator<char> > &'	underlying_functionalities.hpp	115

using StrongA = fluent::NamedType<std::string, struct StrongATag, fluent::MethodCallable>;

struct StringValue
{
    StringValue(StrongA name);
    StrongA id;
};

StringValue::StringValue(StrongA name)
    : id{std::move(name)} {}

Default constructor value behavior change

Since 5f17234 from @knatten , there is a behavior change as well, the underlying type initial value is not the same as before. For example, a POD like int or double would be initialized to 0 before this changeset which could be handy. Now it behaves like a POD by leaving the memory as-is, which is also correct. However this is a big behavior change.

Is there a way we could force the old behavior (prehaps using a new skill)?

Windows MSVC Compile error with std::is_reference

Hi Jonathan, [big fan of fluent c++, got to know you on cppcast]

I get this compile error with MSVC (Visual Studio 2017 15.3). That code is fine on gcc and clang.

/NamedType/named_type.hpp(26): error C2512: 'std::is_reference<T_>': no appropriate default constructor available

explicit NamedTypeImpl(T&& value, typename std::enable_if<!std::is_reference<T_>{}, std::nullptr_t>::type = nullptr)
while the following works (and to me looks more idiomatic):
explicit NamedTypeImpl(T&& value, typename std::enable_if < !std::is_reference<T_>::value, std::nullptr_t > ::type = nullptr) : value_(std::move(value)) {}

Diff is just
std::is_reference<T_>{}
becoming
std::is_reference<T_>::value

Would you accept a PR for that ?

Packaging note for Arch Linux

You might want to note somewhere in the readme that the library is packaged for Arch Linux in the AUR (by me).

Another thing: I've packaged the git version (master), and not the tags, because the project isn't released frequently. It'd be nice if you released a new official version, so that I can make a proper versioned package.

warning in c++20 due to == operator

using Foo = fluent::NamedType<int, struct FooTag, fluent::FunctionCallable, fluent::Arithmetic>;

int main(int argc, char *argv[]) {
  auto a = Foo(1);
  auto b = Foo(0);
  return (a == b) ? 0 : 1;
}

Compile using clang 10, get this warning:

main.cpp:9:13: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'fluent::NamedType<int, FooTag, fluent::FunctionCallable, fluent::Arithmetic>' and 'fluent::NamedType<int, FooTag, fluent::FunctionCallable, fluent::Arithmetic>') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
  return (a == b) ? 0 : 1;
          ~ ^  ~
./underlying_functionalities.hpp:81:20: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
    constexpr bool operator==(T const& other) const { return !(*this < other || *this > other); }

Adding skill to explicitly convert from ArduinoJson JsonVariantConst

I'm using the NamedType library (a simplified copy, using C++11 on an ESP32 platform) to implement a NodeId type, based on an uint16_t.
The configuration of our device is read form a json file, and we're using ArduinoJson library for that. I would like a way to create a NodeId instance from an element of that Json document, a JsonVariantConst instance from ArduinoJson.

I added an extra constructor to the NamedType template class and this works:

NamedType(JsonVariantConst json) : value_(json.as<T>()) {}

The <T> of course refers to the base type of the NamedType, so this only works if ArduinoJson actually supports that type in their as<T>() method.

I think it would be better if I could add such a feature as a Skill so I can also define NamedTypes which do not poses this skill, but my personal templating skills are not good enough to get this done.

Can anybody help me with this?

Concerning bitwise operations

Hello,

First, thank you for this clever coding trick!

Now I have a little improvement to suggest:

  • for the BitWiseLeftShiftable/BitWiseRightShiftable skills, it would be better to use an 'int' as parameter, like:
    template <typename T> struct BitWiseLeftShiftable : crtp<T, fluentBitWiseLeftShiftable> { FLUENT_NODISCARD constexpr T operator<<(const int shift) const { return T(this->underlying().get() << shift); } FLUENT_CONSTEXPR17 T& operator<<=(const int shift) { this->underlying().get() <<= shift; return this->underlying(); } };

  • and also it would be fine to define an aggregate skill, alongside 'struct Arithmetic', for bitwise operations, like
    template<typename T> struct BitWise : BitWiseInvertable<T> , BitWiseAndable<T> , BitWiseOrable<T> , BitWiseXorable<T> , BitWiseLeftShiftable<T> , BitWiseRightShiftable<T> , Comparable<T> {};

Best regards.

Type is template

when I using

template<class T> using Width = NamedType<T, struct WidthTag>;
template<class T> using Height = NamedType<T, struct HeightTag>;
template<class T>
class Rectangle
{
public:
    Rectangle(Width<double> width, Height<double> height) : width_(width.get()), height_(height.get()) {}
    ......
}

so in the main.cpp, I have to write this:

Rectangle<double>  r(Width<double>(10), Height<double>(12));
  • I have to repeat double few times.

when I put it into class:

 
template<class T>
class Rectangle
{
public:
    using Width = NamedType<T, struct WidthTag>;
    using Height = NamedType<T, struct HeightTag>;
    Rectangle(Width width, Height height) : width_(width.get()), height_(height.get()) {}
    ......
}

so in the main.cpp, I have to write this:

Rectangle<double>  r(Rectangle<double>::Width(10), Rectangle<double>::Height<double>(12));

I have a better choice or make it better?

non-const reference to underlying type

It seems like having a get() overload return a non-const reference to the underlying type is a pretty big safety risk, essentially circumventing all the type restrictions put in place with NamedType.

My understanding is that this function is quite widely used by the Skills, which makes sense that they would have access to a non-const reference to the underlying object. But perhaps it would make sense to make the non-const get() overload be protected, so it's only available to the skills, and not part of the public API.

`std::is_constructible` won't compile when `MethodCallable` skill added to NamedType in variant

Consider the following code:

#include <variant>
#include <type_traits>

#include <named_type.hpp>

using test1_t = fluent::NamedType<std::string, struct test_tag, fluent::FunctionCallable>;
using test2_t = fluent::NamedType<std::string, struct test_tag, fluent::MethodCallable>;
using var1_t = std::variant<int, test1_t>;
using var2_t = std::variant<int, test2_t>;

static_assert(std::is_constructible_v<test1_t, std::string>);
static_assert(std::is_constructible_v<var1_t>);
static_assert(std::is_constructible_v<test2_t, std::string>);
static_assert(std::is_constructible_v<var2_t>);

This fails (with gcc 8.3.0, clang 8.0.0 on Ubuntu 18.04), raising the following error as a result of compiling the last line:

externals/named-type/underlying_functionalities.hpp:116:6: error: 'operator->' declared as a pointer to a reference of type 'std::__cxx11::basic_string<char, std::char_traits<char>,     
      std::allocator<char> > &'
    T* operator->() { return std::addressof(this->underlying().get()); }

The full logs are attached - clang.log, gcc.log.

I'm wondering if maybe a remove_cvref is needed somewhere, is this a variant issue or is this an expected error?

Add skill Dereferencable

I use the following skill to add the dereference operator (like in std::optional for example):

template< typename T >
struct Dereferencable;`

template< typename T, typename Parameter, template< typename > class ... Skills >
struct Dereferencable< fluent::NamedType< T, Parameter, Skills... > > : fluent::crtp< fluent::NamedType< T, Parameter, Skills... >, Dereferencable >
{
    constexpr const T& operator*() const &
    {
        return this->underlying().get();
    }

    constexpr T& operator*() &
    {
        return this->underlying().get();
    }
};

Example usage:

void Fct( double value );
...
Fct( *strongType );

Note that I also added [[nodiscard]], but this could be a separate ticket

Overhead?

Hi!
I am using strong types to return PixelColor (3 x uint8) from a image cache library - that is used very intensively.
Is using strong type introducing runtime overhead?
Thank you!

Undesirable inclusion of `<iostream>` for freestanding compilers

It is an undesirable to include <iostream> for freestanding compilers. This is especially true for embedded devices that do not have a cout. As you can see in this example https://godbolt.org/z/8vrdv8 call to std::ios_base::Init::Init() cannot be optimized even if you dont use cout and even at maximum optimization level. But this is still add overhead by increasing compilation time, producing larger binaries and increasing startup time.

gcc 9.3 claims operator++ is ambiguous

I added the following test case to tests.cpp, which is just a trivial combination of the contents of the PreIncrementable and PostIncrementable tests:

TEST_CASE("PreAndPostIncrementable")
{
    using StrongInt = fluent::NamedType<int, struct StrongIntTag, fluent::PreIncrementable, fluent::PostIncrementable>;
    {
        StrongInt a{1};
        StrongInt b = ++a;
        CHECK( a.get() == 2 );
        CHECK( b.get() == 2 );
    }
    {
        StrongInt a{1};
        StrongInt b = a++;
        CHECK( a.get() == 2 );
        CHECK( b.get() == 1 );
    }
}

gcc 9.3 fails to compile it, saying "error: request for member ‘operator++’ is ambiguous".

I thought I would try fixing it myself but so far I haven't been able to figure out why it doesn't work, let alone how to fix it. This problem also affects any type that uses Arithmetic, which is how I originally discovered it.

Tag a new version

I'm currently trying to have this library as part of the new Conan Center Index, and it would be convenient to have a new tag for this library. It makes versioning easier and there has been plenty of improvements since the last tag.

do the arguments require the correct order?

hi,

using this sample code:

using FirstName = NamedType<std::string, struct FirstNameTag>;
using LastName = NamedType<std::string, struct LastNameTag>;

static const FirstName::argument firstName;
static const LastName::argument lastName;

void displayName(FirstName const& theFirstName, LastName const& theLastName)
{}

// Call site
displayName(lastName = "Doe", firstName = "John");

it looks I can't swap args order because of compiler error.
should it be so? or am I doing smth wrong?

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.