├── .gitignore ├── README.md ├── catch.hpp ├── defaulted.h ├── main.cpp └── optional.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Defaulted 2 | 3 | The purpose of `Defaulted` is to work around two constraints in C++ related to default parameters: 4 | - The first one is that all the default parameters have to be at the end of the arguments list of a function. This can make an interface less natural, because arguments are no longer grouped in a logical order. Instead, they are grouped in a technical order: the non-default parameters first, then the default ones, which can be confusing at call site. 5 | - The second constraint is their interdependence: if there are several default parameters , and a call site wants to pass a value for only one of them, it also has to provide a value for all the other default parameters preceding it in the parameters list of the function. This again makes for awkward call sites. 6 | 7 | 8 | _The components shown in this document are located int the `fluent` namespace._ 9 | 10 | ## Basic usage 11 | 12 | ### Placing default parameters between other parameters 13 | 14 | Consider the following prototype: 15 | 16 | ```cpp 17 | void f(int x, int y, int z); 18 | ``` 19 | 20 | To add a default value to a parameter in the middle of a function prototype we normally have to put it at the end: 21 | 22 | ```cpp 23 | void f(int x, int z, int y = 42); 24 | ``` 25 | 26 | `Defaulted` allows to place it anywhere: 27 | 28 | ```cpp 29 | void f(int x, Defaulted y, int y) 30 | ``` 31 | 32 | This can be called this way: 33 | 34 | ```cpp 35 | f(0, defaultValue, 1); // equivalent to f(0, 42, 1); 36 | ``` 37 | 38 | Or with a specific value: 39 | 40 | ```cpp 41 | f(0, 35, 1); 42 | ``` 43 | 44 | And the implementation of `f` can retrieve the value inside the `Defaulted` by calling its `.get()` method. 45 | 46 | ### Specifying the value of only one default parameter 47 | 48 | In the case of several default parameters, we normally have to specify the values for the default parameters preceding it: 49 | 50 | ```cpp 51 | void f(int x, int y = 42, int z = 43); 52 | 53 | f(0, 42, 1); // we have to write the default value 42 at call site to be able to pass 1 to z 54 | ``` 55 | 56 | With Defaulted we don't have to: 57 | 58 | ```cpp 59 | void f(int x, Defaulted y, Defaulted z); 60 | 61 | f(0, defaultValue, 1); 62 | ``` 63 | 64 | ## Values that don't fit into a template 65 | 66 | `int`s like 42 fit into a template such as `Defaulted`, but most other types don't. 67 | For other types, a variation of `Defaulted` called `DefaultedF` accept the default value wrapped into a function, itself wrapped into a type: 68 | 69 | ```cpp 70 | struct GetDefaultAmount{ static double get(){ return 45.6; } }; 71 | 72 | void g(int x, DefaultedF y, int z) 73 | ``` 74 | 75 | ##Default parameters that depend on other parameters 76 | 77 | Since C++ does not allow default values to depend on other parameters: 78 | ```cpp 79 | void f(double x, double y, double z = x + y) // imaginary C++ 80 | { 81 | std::cout << "x = " << x << '\n' 82 | << "y = " << y << '\n' 83 | << "z = " << z << '\n'; 84 | } 85 | ``` 86 | 87 | `DefaultedF` allows default functions to accepts parameters, to which a function can pass the other parameters: 88 | 89 | ```cpp 90 | struct GetDefaultAmount{ static double get(double x, double y){ return x + y; } }; 91 | 92 | void f(double x, double y, DefaultedF z) 93 | { 94 | std::cout << "x = " << x << '\n' 95 | << "y = " << y << '\n' 96 | << "z = " << z.get_or_default(x, y) << '\n'; 97 | } 98 | ``` 99 | 100 | ## The particular case of the default default value 101 | 102 | A pretty common case for default parameters is when they take the value resulting from a call to the default constructor of their type: `T()`. 103 | 104 | To make this easier to express in an interface, we adopt the convention that if no value is passed in the Defaulted template, then it falls back to calling the default constructor of its underlying type, for a default value: 105 | 106 | ```cpp 107 | void f(int x, Defaulted y, int z); // equivalent to pseudo-C++ void f(int x, std::string y = std::string(), int z); 108 | ``` 109 | 110 | ## Passing default parameters by const reference 111 | 112 | To emulate a default parameter on a const reference, we use a `Defaulted` on a const reference: 113 | 114 | ```cpp 115 | void h(int x, Defaulted y, int z) 116 | ``` 117 | which does not make a copy of the parameter passed, when there is one. 118 | -------------------------------------------------------------------------------- /defaulted.h: -------------------------------------------------------------------------------- 1 | #ifndef defaulted_h 2 | #define defaulted_h 3 | 4 | #include "optional.hpp" 5 | 6 | namespace fluent 7 | { 8 | 9 | struct DefaultValue{}; 10 | static const DefaultValue defaultValue; 11 | 12 | template 13 | using IsNotReference = typename std::enable_if::value, void>::type; 14 | 15 | template 16 | class Defaulted 17 | { 18 | public: 19 | Defaulted(T const& t) : value_(t){} 20 | template> 21 | Defaulted(T&& t) : value_(std::move(t)){} 22 | Defaulted(DefaultValue) : value_(DefaultedParameter...) {} 23 | T const& get_or_default() const { return value_; } 24 | T & get_or_default() { return value_; } 25 | private: 26 | T value_; 27 | }; 28 | 29 | template 30 | class DefaultedF 31 | { 32 | public: 33 | DefaultedF(T const& t) : value_(t){} 34 | template> 35 | DefaultedF(T&& t) : value_(std::move(t)){} 36 | DefaultedF(DefaultValue) : value_(fluent::nullopt) {} 37 | 38 | template 39 | T get_or_default(Args&&... args) 40 | { 41 | if (value_) 42 | { 43 | return *value_; 44 | } 45 | else 46 | { 47 | return GetDefaultValue::get(std::forward(args)...); 48 | } 49 | } 50 | private: 51 | fluent::optional value_; 52 | }; 53 | 54 | } 55 | 56 | 57 | #endif /* defaulted_h */ 58 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "defaulted.h" 3 | 4 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 5 | #include "catch.hpp" 6 | 7 | int f1(int , fluent::Defaulted y, int ) 8 | { 9 | return y.get_or_default(); 10 | } 11 | 12 | TEST_CASE("Default value", "[Defaulted]") 13 | { 14 | REQUIRE(f1(0, fluent::defaultValue, 1) == 42); 15 | } 16 | 17 | TEST_CASE("Value provided", "[Defaulted]") 18 | { 19 | REQUIRE(f1(0, 43, 1) == 43); 20 | } 21 | 22 | std::string f2(int , fluent::Defaulted y, int ) 23 | { 24 | return y.get_or_default(); 25 | } 26 | 27 | TEST_CASE("Default constructor", "[Defaulted]") 28 | { 29 | REQUIRE(f2(0, fluent::defaultValue, 1) == std::string()); 30 | } 31 | 32 | TEST_CASE("Default constructor value provided", "[Defaulted]") 33 | { 34 | REQUIRE(f2(0, std::string("hello"), 1) == "hello"); 35 | } 36 | 37 | struct GetDefaultAmount{ static double get(){ return 45.6; } }; 38 | 39 | double f3(int x, fluent::DefaultedF y, int z) 40 | { 41 | return y.get_or_default(); 42 | } 43 | 44 | TEST_CASE("DefaultedF Default value", "[DefaultedF]") 45 | { 46 | REQUIRE(f3(0, fluent::defaultValue, 1) == 45.6); 47 | } 48 | 49 | TEST_CASE("DefaultedF Value provided", "[DefaultedF]") 50 | { 51 | REQUIRE(f3(0, 13.4, 1) == 13.4); 52 | } 53 | 54 | class CopyLogger 55 | { 56 | public: 57 | CopyLogger(int& copyCount) : copyCount_(copyCount){} 58 | CopyLogger(CopyLogger const& other) : copyCount_(other.copyCount_){ ++copyCount_; } 59 | private: 60 | int& copyCount_; 61 | }; 62 | 63 | void f4(fluent::Defaulted){} 64 | 65 | TEST_CASE("Defaulted makes a copy", "[Defaulted]") 66 | { 67 | int copyCount = 0; 68 | f4(CopyLogger(copyCount)); 69 | REQUIRE(copyCount == 1); 70 | } 71 | 72 | void f5(fluent::Defaulted){} 73 | 74 | TEST_CASE("Defaulted const ref makes no copy", "[Defaulted]") 75 | { 76 | int copyCount = 0; 77 | f5(CopyLogger(copyCount)); 78 | REQUIRE(copyCount == 0); 79 | } 80 | 81 | struct GetDefaultZ 82 | { 83 | static int get(int x, int y) { return x + y; } 84 | }; 85 | 86 | int f6(int x, int y, fluent::DefaultedF z) 87 | { 88 | return z.get_or_default(x, y); 89 | } 90 | 91 | TEST_CASE("DefaultedF on dependent parameter", "[Defaulted]") 92 | { 93 | REQUIRE(f6(1, 5, fluent::defaultValue) == 6); 94 | } 95 | -------------------------------------------------------------------------------- /optional.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // optional.hpp 3 | // Defaulted 4 | // 5 | // Created by Jonathan Boccara on 18/11/2017. 6 | // Copyright © 2017 Jonathan Boccara. All rights reserved. 7 | // 8 | 9 | #ifndef optional_h 10 | #define optional_h 11 | 12 | namespace fluent 13 | { 14 | 15 | struct nullopt_t {}; 16 | static const nullopt_t nullopt; 17 | 18 | template 19 | class optional 20 | { 21 | public: 22 | optional(const T& object) : m_initialized(true) { new (m_object) T(object); }; 23 | optional(nullopt_t) : m_initialized(false) {}; 24 | optional() : m_initialized(false) {}; 25 | optional(const optional& other) : m_initialized(other.m_initialized) { if (other.m_initialized) new (m_object) T(*other); } 26 | optional& operator=(const optional& other) 27 | { 28 | clear(); 29 | new (m_object) T(*other); 30 | m_initialized = other.m_initialized; 31 | return *this; 32 | } 33 | optional& operator=(nullopt_t) { clear(); return *this; } 34 | ~optional() { clear(); } 35 | operator bool() const { return m_initialized; }; 36 | T& operator*() { return *reinterpret_cast(m_object); }; 37 | const T& operator*() const { return *reinterpret_cast(m_object); }; 38 | T* operator->() { return &**this; }; 39 | const T* operator->() const { return &**this; }; 40 | private: 41 | void clear() { if (m_initialized) (&**this)->~T(); m_initialized = false; } 42 | private: 43 | char m_object[sizeof(T)]; 44 | bool m_initialized; 45 | }; 46 | 47 | } 48 | 49 | #endif /* optional_h */ 50 | --------------------------------------------------------------------------------