Skip to content
Snippets Groups Projects
Commit 0f341dd2 authored by Whitney Armstrong's avatar Whitney Armstrong
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
---
Language: Cpp
BasedOnStyle: Chromium
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
RawStringFormats:
- Delimiter: pb
Language: TextProto
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...
MIT License
Copyright (c) 2017 Jonathan Boccara
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
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++](https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/).
## Basic usage
It central piece is the templated class NamedType, which can be used to declare a strong type with a typedef-like syntax:
```cpp
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:
```cpp
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:
```cpp
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:
```cpp
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:
```cpp
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:
```cpp
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:
```cpp
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 main.cpp for usage examples.
This diff is collapsed.
#ifndef CRTP_HPP
#define CRTP_HPP
namespace fluent
{
template <typename T, template<typename> class crtpType>
struct crtp
{
T& underlying() { return static_cast<T&>(*this); }
T const& underlying() const { return static_cast<T const&>(*this); }
};
} // namespace fluent
#endif
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include <cmath>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include "named_type.hpp"
// Usage examples
template<typename T>
decltype(auto) tee(T&& value)
{
std::cout << value << '\n';
return std::forward<T>(value);
}
using Meter = fluent::NamedType<double, struct MeterParameter, fluent::Addable, fluent::Comparable>;
Meter operator"" _meter(unsigned long long value) { return Meter(value); }
//Meter operator"" _meter(long double value) { return Meter(value); }
using Width = fluent::NamedType<Meter, struct WidthParameter>;
using Height = fluent::NamedType<Meter, struct HeightParameter>;
class Rectangle
{
public:
Rectangle(Width width, Height height) : width_(width.get()), height_(height.get()) {}
Meter getWidth() const { return width_; }
Meter getHeight() const { return height_; }
private:
Meter width_;
Meter height_;
};
TEST_CASE("Basic usage")
{
Rectangle r(Width(10_meter), Height(12_meter));
REQUIRE(r.getWidth().get() == 10);
REQUIRE(r.getHeight().get() == 12);
}
using NameRef = fluent::NamedType<std::string&, struct NameRefParameter>;
void changeValue(const NameRef name)
{
name.get() = "value2";
}
TEST_CASE("Passing a strong reference")
{
std::string value = "value1";
changeValue(NameRef(value));
REQUIRE(value == "value2");
}
TEST_CASE("Construction of NamedType::ref from the underlying type")
{
using StrongInt = fluent::NamedType<int, struct StrongIntTag>;
auto addOne = [](StrongInt::ref si) { ++(si.get()); };
int i = 42;
addOne(StrongInt::ref(i));
REQUIRE(i == 43);
}
TEST_CASE("Implicit conversion of NamedType to NamedType::ref")
{
using StrongInt = fluent::NamedType<int, struct StrongIntTag>;
auto addOne = [](StrongInt::ref si) { ++(si.get()); };
StrongInt i(42);
addOne(i);
REQUIRE(i.get() == 43);
StrongInt j(42);
addOne(StrongInt::ref(j));
REQUIRE(j.get() == 43);
}
template<typename Function>
using Comparator = fluent::NamedType<Function, struct ComparatorParameter>;
template <typename Function>
std::string performAction(Comparator<Function> comp)
{
return comp.get()();
}
TEST_CASE("Strong generic type")
{
REQUIRE(performAction(fluent::make_named<Comparator>([](){ return std::string("compare"); })) == "compare");
}
TEST_CASE("Addable")
{
using AddableType = fluent::NamedType<int, struct SubtractableTag, fluent::Addable>;
AddableType s1(12);
AddableType s2(10);
REQUIRE((s1 + s2).get() == 22);
}
TEST_CASE("Subtractable")
{
using SubtractableType = fluent::NamedType<int, struct SubtractableTag, fluent::Subtractable>;
SubtractableType s1(12);
SubtractableType s2(10);
REQUIRE((s1 - s2).get() == 2);
}
TEST_CASE("Multiplicable")
{
using MultiplicableType = fluent::NamedType<int, struct MultiplicableTag, fluent::Multiplicable>;
MultiplicableType s1(12);
MultiplicableType s2(10);
REQUIRE((s1 * s2).get() == 120);
}
TEST_CASE("Comparable")
{
REQUIRE((10_meter == 10_meter));
REQUIRE(!(10_meter == 11_meter));
REQUIRE((10_meter != 11_meter));
REQUIRE(!(10_meter != 10_meter));
REQUIRE((10_meter < 11_meter));
REQUIRE(!(10_meter < 10_meter));
REQUIRE((10_meter <= 10_meter));
REQUIRE((10_meter <= 11_meter));
REQUIRE(!(10_meter <= 9_meter));
REQUIRE((11_meter > 10_meter));
REQUIRE(!(10_meter > 11_meter));
REQUIRE((11_meter >= 10_meter));
REQUIRE((10_meter >= 10_meter));
REQUIRE(!(9_meter >= 10_meter));
}
TEST_CASE("ConvertibleWithOperator")
{
struct B
{
B(int x) : x(x) {}
int x;
};
struct A
{
A(int x) : x(x) {}
operator B () const { return B(x);}
int x;
};
using StrongA = fluent::NamedType<A, struct StrongATag, fluent::ImplicitlyConvertibleTo<B>::templ>;
StrongA strongA(A(42));
B b = strongA;
REQUIRE(b.x == 42);
}
TEST_CASE("ConvertibleWithConstructor")
{
struct A
{
A(int x) : x(x) {}
int x;
};
struct B
{
B(A a) : x(a.x) {}
int x;
};
using StrongA = fluent::NamedType<A, struct StrongATag, fluent::ImplicitlyConvertibleTo<B>::templ>;
StrongA strongA(A(42));
B b = strongA;
REQUIRE(b.x == 42);
}
TEST_CASE("ConvertibleToItself")
{
using MyInt = fluent::NamedType<int, struct MyIntTag, fluent::ImplicitlyConvertibleTo<int>::templ>;
MyInt myInt(42);
int i = myInt;
REQUIRE(i == 42);
}
TEST_CASE("Hash")
{
using SerialNumber = fluent::NamedType<std::string, struct SerialNumberTag, fluent::Comparable, fluent::Hashable>;
std::unordered_map<SerialNumber, int> hashMap = { {SerialNumber{"AA11"}, 10}, {SerialNumber{"BB22"}, 20} };
SerialNumber cc33{"CC33"};
hashMap[cc33] = 30;
REQUIRE(hashMap[SerialNumber{"AA11"}] == 10);
REQUIRE(hashMap[SerialNumber{"BB22"}] == 20);
REQUIRE(hashMap[cc33] == 30);
}
struct testFunctionCallable_A
{
testFunctionCallable_A(int x) : x(x) {}
testFunctionCallable_A(testFunctionCallable_A const&) = delete; // ensures that passing the argument to a function doesn't make a copy
testFunctionCallable_A(testFunctionCallable_A&&) = default;
testFunctionCallable_A& operator+=(testFunctionCallable_A const& other) { x += other.x; return *this; }
int x;
};
testFunctionCallable_A operator+(testFunctionCallable_A const& a1, testFunctionCallable_A const& a2)
{
return testFunctionCallable_A(a1.x + a2.x);
}
bool operator==(testFunctionCallable_A const& a1, testFunctionCallable_A const& a2)
{
return a1.x == a2.x;
}
TEST_CASE("Function callable")
{
using A = testFunctionCallable_A;
auto functionTakingA = [](A const& a){ return a.x; };
using StrongA = fluent::NamedType<A, struct StrongATag, fluent::FunctionCallable>;
StrongA strongA(A(42));
const StrongA constStrongA(A(42));
REQUIRE(functionTakingA(strongA) == 42);
REQUIRE(functionTakingA(constStrongA) == 42);
REQUIRE(strongA + strongA == 84);
}
TEST_CASE("Method callable")
{
class A
{
public:
A(int x) : x(x) {}
A(A const&) = delete; // ensures that invoking a method doesn't make a copy
A(A&&) = default;
int method(){ return x; }
int constMethod() const{ return x; }
private:
int x;
};
using StrongA = fluent::NamedType<A, struct StrongATag, fluent::MethodCallable>;
StrongA strongA(A(42));
const StrongA constStrongA(A((42)));
REQUIRE(strongA->method() == 42);
REQUIRE(constStrongA->constMethod() == 42);
}
TEST_CASE("Callable")
{
class A
{
public:
A(int x) : x(x) {}
A(A const&) = delete; // ensures that invoking a method or function doesn't make a copy
A(A&&) = default;
int method(){ return x; }
int constMethod() const{ return x; }
private:
int x;
};
auto functionTakingA = [](A const& a){ return a.constMethod(); };
using StrongA = fluent::NamedType<A, struct StrongATag, fluent::Callable>;
StrongA strongA(A(42));
const StrongA constStrongA(A((42)));
REQUIRE(functionTakingA(strongA) == 42);
REQUIRE(strongA->method() == 42);
REQUIRE(constStrongA->constMethod() == 42);
}
TEST_CASE("Named arguments")
{
using FirstName = fluent::NamedType<std::string, struct FirstNameTag>;
using LastName = fluent::NamedType<std::string, struct LastNameTag>;
static const FirstName::argument firstName;
static const LastName::argument lastName;
auto getFullName = [](FirstName const& firstName, LastName const& lastName)
{
return firstName.get() + lastName.get();
};
auto fullName = getFullName(firstName = "James", lastName = "Bond");
REQUIRE(fullName == "JamesBond");
}
TEST_CASE("Empty base class optimization")
{
REQUIRE(sizeof(Meter) == sizeof(double));
}
#ifndef NAMED_TYPE_HPP
#define NAMED_TYPE_HPP
#include "named_type_impl.hpp"
#include "underlying_functionalities.hpp"
#endif
#ifndef named_type_impl_h
#define named_type_impl_h
#include <type_traits>
// Enable empty base class optimization with multiple inheritance on Visual Studio.
#if defined(_MSC_VER) && _MSC_VER >= 1910
# define FLUENT_EBCO __declspec(empty_bases)
#else
# define FLUENT_EBCO
#endif
namespace fluent
{
template<typename T>
using IsNotReference = typename std::enable_if<!std::is_reference<T>::value, void>::type;
template <typename T, typename Parameter, template<typename> class... Skills>
class FLUENT_EBCO NamedType : public Skills<NamedType<T, Parameter, Skills...>>...
{
public:
using UnderlyingType = T;
// constructor
explicit constexpr NamedType(T const& value) : value_(value) {}
template<typename T_ = T, typename = IsNotReference<T_>>
explicit constexpr NamedType(T&& value) : value_(std::move(value)) {}
// get
constexpr T& get() { return value_; }
constexpr T const& get() const {return value_; }
// conversions
using ref = NamedType<T&, Parameter, Skills...>;
operator ref ()
{
return ref(value_);
}
struct argument
{
template<typename U>
NamedType operator=(U&& value) const
{
return NamedType(std::forward<U>(value));
}
argument() = default;
argument(argument const&) = delete;
argument(argument &&) = delete;
argument& operator=(argument const&) = delete;
argument& operator=(argument &&) = delete;
};
private:
T value_;
};
template<template<typename T> class StrongType, typename T>
constexpr StrongType<T> make_named(T const& value)
{
return StrongType<T>(value);
}
} // namespace fluent
#endif /* named_type_impl_h */
#ifndef UNDERLYING_FUNCTIONALITIES_HPP
#define UNDERLYING_FUNCTIONALITIES_HPP
#include "crtp.hpp"
#include "named_type_impl.hpp"
#include <functional>
#include <iostream>
#include <memory>
namespace fluent
{
template <typename T>
struct Incrementable : crtp<T, Incrementable>
{
T& operator+=(T const& other) { this->underlying().get() += other.get(); return this->underlying(); }
};
template <typename T>
struct PreIncrementable : crtp<T, PreIncrementable>
{
T& operator++() { ++this->underlying().get(); return this->underlying(); }
};
template <typename T>
struct Addable : crtp<T, Addable>
{
T operator+(T const& other) const { return T(this->underlying().get() + other.get()); }
};
template <typename T>
struct Subtractable : crtp<T, Subtractable>
{
T operator-(T const& other) const { return T(this->underlying().get() - other.get()); }
};
template <typename T>
struct Multiplicable : crtp<T, Multiplicable>
{
T operator*(T const& other) const { return T(this->underlying().get() * other.get()); }
};
template <typename T>
struct Comparable : crtp<T, Comparable>
{
bool operator<(T const& other) const { return this->underlying().get() < other.get(); }
bool operator>(T const& other) const { return other.get() < this->underlying().get(); }
bool operator<=(T const& other) const { return !(other.get() < this->underlying().get());}
bool operator>=(T const& other) const { return !(*this < other); }
bool operator==(T const& other) const { return !(*this < other) && !(other.get() < this->underlying().get()); }
bool operator!=(T const& other) const { return !(*this == other); }
};
template <typename T>
struct Printable : crtp<T, Printable>
{
void print(std::ostream& os) const { os << this->underlying().get(); }
};
template <typename Destination>
struct ImplicitlyConvertibleTo
{
template <typename T>
struct templ : crtp<T, templ>
{
operator Destination() const
{
return this->underlying().get();
}
};
};
template <typename T, typename Parameter, template<typename> class... Skills>
std::ostream& operator<<(std::ostream& os, NamedType<T, Parameter, Skills...> const& object)
{
object.print(os);
return os;
}
template<typename T>
struct Hashable
{
static constexpr bool is_hashable = true;
};
template<typename NamedType_>
struct FunctionCallable;
template <typename T, typename Parameter, template<typename> class... Skills>
struct FunctionCallable<NamedType<T, Parameter, Skills...>> : crtp<NamedType<T, Parameter, Skills...>, FunctionCallable>
{
operator T const&() const
{
return this->underlying().get();
}
operator T&()
{
return this->underlying().get();
}
};
template<typename NamedType_>
struct MethodCallable;
template <typename T, typename Parameter, template<typename> class... Skills>
struct MethodCallable<NamedType<T, Parameter, Skills...>> : crtp<NamedType<T, Parameter, Skills...>, MethodCallable>
{
T const* operator->() const { return std::addressof(this->underlying().get()); }
T* operator->() { return std::addressof(this->underlying().get()); }
};
template<typename NamedType_>
struct Callable : FunctionCallable<NamedType_>, MethodCallable<NamedType_>{};
} // namespace fluent
namespace std
{
template <typename T, typename Parameter, template<typename> class... Skills>
struct hash<fluent::NamedType<T, Parameter, Skills...>>
{
using NamedType = fluent::NamedType<T, Parameter, Skills...>;
using checkIfHashable = typename std::enable_if<NamedType::is_hashable, void>::type;
size_t operator()(fluent::NamedType<T, Parameter, Skills...> const& x) const
{
return std::hash<T>()(x.get());
}
};
}
#endif
test
g++ -o test -std=c++17 func_pattern.cxx -I.
g++ -S -o test.asm -std=c++17 func_pattern.cxx -I. -O3
#include <array>
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <cmath>
// See the excellent Fluentcpp blog series on strong types.
// https://www.fluentcpp.com/2017/05/23/strong-types-inheriting-functionalities-from-underlying/
#include "NamedType/named_type.hpp"
using namespace fluent;
#include <stdio.h>
namespace func {
//@{
/** Helpers.
*/
template <typename T>
struct is_tuple_impl : std::false_type {};
template <typename... Ts>
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
template <typename T>
struct is_tuple : is_tuple_impl<std::decay_t<T>> {};
//@}
/** Named strong type helper.
*
*/
template <typename Tag>
using Var = NamedType<double, Tag, Addable, Multiplicable, Printable,
ImplicitlyConvertibleTo<double>::templ>;
/** Forward declaration of mixin.
*
*/
template <typename>
struct ConstructedFunctionSet;
/** FunctionSet: new variables defined by functions.
*
* \param FVars should be a FVariables template class.
* \param Fs is a tuple of lambdas that calculate values of NewVars_t
* types.
*
*/
template <typename FVars, typename Fs,
typename = typename std::enable_if<is_tuple<Fs>::value>::type>
struct FunctionSet : FVars {
using IndependentVariables_t = typename FVars::IndependentVariables_t;
using InputVars_t = typename FVars::InputVars_t;
using NewVars_t = typename FVars::NewVars_t;
using Vars_t = typename FVars::Vars_t;
using VariableFuncs_t = Fs;
static const size_t N_input_vars = FVars::N_input_vars;
static const size_t N_new_vars = FVars::N_new_vars;
static const size_t N_vars = FVars::N_vars;
VariableFuncs_t functions;
FunctionSet(const FVars& u, const Fs& f) : FVars(u), functions(f) {}
FunctionSet(const FunctionSet& fs) : FVars(fs), functions(fs.functions) {}
//void Print() const {}
constexpr Vars_t ComputeValues(const IndependentVariables_t& v) const {
if constexpr (std::is_same<IndependentVariables_t, InputVars_t>::value) {
return std::tuple_cat(v, ComputeNewVars(v));
} else {
auto vars = FVars::input_variable_set.ComputeValues(v);
return std::tuple_cat(vars, ComputeNewVars(vars));
}
}
template <std::size_t I, typename F, typename Args>
static constexpr auto eval_vars2(const F& f, const Args& a) {
using Return_t = typename std::tuple_element<I, NewVars_t>::type;
return Return_t{f(a)};
}
template <typename Funcs, typename Args, std::size_t... I>
static constexpr auto eval_vars(const Funcs& f, const Args& a, std::index_sequence<I...>) {
return std::make_tuple(eval_vars2<I>(std::get<I>(f), a)...);
}
constexpr auto ComputeNewVars(const InputVars_t& v) const {
return eval_vars(functions, v, std::make_index_sequence<N_new_vars>{});
}
constexpr auto operator()(const InputVars_t& v) const { return ComputeNewVars(v); }
};
/** Definable Function Variables.
* Used to construct a FunctionSet from lambdas and wraps the lambda
* arguments with a set of standard variables.
*
* \param FVars Function variables.
* \param Mixin Mixin which adds the ability to declare/define more variables
* to FVariables..
*
*/
template <typename FVars, template <typename> class Mixin>
struct Definable : FVars {
using InputVars_t = typename FVars::InputVars_t;
using NewVars_t = typename FVars::NewVars_t;
//using FVars::Print;
template <typename... Ts>
Definable(Ts&&... t) : FVars(std::forward<Ts>(t)...) {}
template <typename... Funcs>
constexpr auto operator()(const Funcs&... fs) const noexcept {
return t2t_impl(*this, std::make_tuple(fs...), std::make_index_sequence<sizeof...(fs)>{});
}
template <typename TUP, std::size_t... I>
static constexpr auto t2t_impl(const Definable<FVars, Mixin>& fd, const TUP& t,
std::index_sequence<I...>) noexcept {
constexpr auto l_wrap = [](const auto& f) constexpr {
return [&](const InputVars_t& args) { return f(args); };
};
using type = decltype(std::make_tuple(l_wrap(std::get<I>(t))...));
return Mixin<FunctionSet<FVars, type>>(FVars{fd}, std::make_tuple(l_wrap(std::get<I>(t))...));
}
};
/** Function Variables (to be defined with a lambda).
*
* \param VarSet is the previously defined variables
* \param NewVars... is a pack of new types computed from the InputVars.
*
* Note the functions relating the new to the old have not yet been
* specified..
*
*/
template <typename VarSet, typename... NewVars>
struct FVariables {
using VarSet_t = VarSet;
using IndependentVariables_t = typename VarSet::IndependentVariables_t;
using InputVars_t = typename VarSet::Vars_t;
using NewVars_t = std::tuple<NewVars...>;
using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>()));
static const size_t N_input_vars = std::tuple_size<InputVars_t>::value;
static const size_t N_new_vars = std::tuple_size<NewVars_t>::value;
static const size_t N_vars = std::tuple_size<Vars_t>::value;
VarSet_t input_variable_set;
FVariables() noexcept {}
FVariables(const VarSet& f) noexcept : input_variable_set(f) {}
FVariables(const FVariables& t) noexcept : input_variable_set(t.input_variable_set) {}
//void Print() const {
// // std::cout << __PRETTY_FUNCTION__ << "\n";
//}
};
/** Function variable specialization where the variable is an independent variable nad thus has no
* lambda to calculate it. The pack Vs should be left empty.
*/
template <typename... InVars, typename... Vs>
struct FVariables<std::tuple<InVars...>, Vs...> {
using IndependentVariables_t = std::tuple<InVars...>;
using InputVars_t = IndependentVariables_t;
using NewVars_t = std::tuple<Vs...>;
using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>()));
static const size_t N_input_vars = std::tuple_size<InputVars_t>::value;
static const size_t N_new_vars = std::tuple_size<NewVars_t>::value;
static const size_t N_vars = std::tuple_size<Vars_t>::value;
constexpr FVariables() noexcept {}
//void Print() const {
// // std::cout << __PRETTY_FUNCTION__ << "\n";
//}
};
/** Mixin class for FunctionSet that adds the ability to add more variables.
* \param M is a FunctionSet< FV<FS,NewVars>, lambdas... >
*/
template <typename M>
struct ConstructedFunctionSet : M {
ConstructedFunctionSet(const ConstructedFunctionSet& fs) : M(fs) {}
template <typename... Ts>
constexpr ConstructedFunctionSet(Ts&&... t) noexcept : M(std::forward<Ts>(t)...) {}
template <typename... Vs>
constexpr auto make_vars() const noexcept {
return Definable<FVariables<M, Vs...>, ConstructedFunctionSet>(M(*this));
}
template <typename... Vs, typename... Fs>
constexpr auto add(const Fs&... fs) const noexcept {
return make_vars<Vs...>()(fs...);
}
};
template <typename T, typename... Vs>
auto make_vars() {
return Definable<FVariables<T, Vs...>, ConstructedFunctionSet>{};
}
/** Helper making independent vars using FVariables specialization.
*/
template <typename... Vs>
auto make_independent_vars() {
return Definable<FVariables<std::tuple<Vs...>>, ConstructedFunctionSet>{}();
}
template <class...>
struct is_FunctionSet : public std::false_type {};
template <class... Vs>
struct is_FunctionSet<ConstructedFunctionSet<Vs...>> : public std::true_type {};
template <class... Vs>
struct is_FunctionSet<FunctionSet<Vs...>> : public std::true_type {};
} // namespace func
#pragma pack(1)
struct xxVar {
long x, y;
};
int main() {
{
xxVar v = {1081610240, 1081610240};
printf("%f\n", *(float *)&v);
}
using namespace func;
using X_coord = Var<struct X_coord_tag>;
using Y_coord = Var<struct Y_coord_tag>;
using Z_coord = Var<struct Z_coord_tag>;
using A_coord = Var<struct A_coord_tag>;
using B_coord = Var<struct B_coord_tag>;
using C_coord = Var<struct C_coord_tag>;
using D_coord = Var<struct D_coord_tag>;
using E_coord = Var<struct E_coord_tag>;
using F_coord = Var<struct F_coord_tag>;
//auto res0 = make_vars<std::tuple<X_coord>, Y_coord>();
//auto res2 = res0([](const auto& x) { return 11.0 * std::get<X_coord>(x); });
//res2.Print();
//auto res3 = res2.add<Z_coord>([](const auto& x) {
// return 0.2 * std::get<X_coord>(x) + std::get<Y_coord>(x) * std::get<Y_coord>(x);
//});
//auto res4 = res3(std::make_tuple(X_coord{5}, Y_coord{0.1}));
//std::cout << std::get<0>(res4) << "\n";
//auto res6 = res3.ComputeValues(std::make_tuple(X_coord{0.3}));
//std::cout << std::get<0>(res6) << "\n";
//std::cout << std::get<1>(res6) << "\n";
//std::cout << std::get<2>(res6) << "\n";
//auto res5 =
// res3.add<A_coord, B_coord, C_coord>([](const auto& x) { return 1.0 * std::get<X_coord>(x); },
// [](const auto& x) { return 2.0 * std::get<Y_coord>(x); },
// [](const auto& x) { return 3.0 * std::get<Z_coord>(x); });
//auto res7 = res5.ComputeValues(std::make_tuple(X_coord{0.3}));
//std::cout << std::get<0>(res7) << "\n";
//std::cout << std::get<1>(res7) << "\n";
//std::cout << std::get<2>(res7) << "\n";
//std::cout << std::get<3>(res7) << "\n";
//std::cout << std::get<4>(res7) << "\n";
//std::cout << std::get<5>(res7) << "\n";
//std::cout << " ------------------- \n";
//std::cout << std::get<X_coord>(res7) << "\n";
//std::cout << std::get<Y_coord>(res7) << "\n";
//std::cout << std::get<Z_coord>(res7) << "\n";
//std::cout << std::get<A_coord>(res7) << "\n";
//std::cout << std::get<B_coord>(res7) << "\n";
//std::cout << std::get<C_coord>(res7) << "\n";
//// auto res = fs0.add<Y_coord>( [](const auto& x) { return
//// 0.2*std::get<X_coord>(x) ; }); res.Print(); fs0.add<Y_coord>([](const
//// auto& x) { return 0.2 * std::get<X_coord>(x); });
//// auto fs0 = make_FunctionSet<aTag>([](const auto& x) {return 0.1;}
auto vars0 = make_independent_vars<X_coord, Y_coord>();
auto vars1 = vars0.add<Z_coord>([](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
return x + y;
});
auto vars2 = vars1.add<A_coord, B_coord, C_coord>(
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
return x * y * z;
},
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
return x + y + z;
},
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
return 1.0/(x * y * z);
});
//auto values1 = vars1.ComputeValues(std::make_tuple(X_coord{2.0}, Y_coord{3.0}));
//std::cout << std::get<0>(values1) << "\n";
//std::cout << std::get<1>(values1) << "\n";
//std::cout << std::get<2>(values1) << "\n";
//auto values2 = vars2.ComputeValues(std::make_tuple(X_coord{2.0}, Y_coord{3.0}));
//std::cout << std::get<0>(values2) << "\n";
//std::cout << std::get<1>(values2) << "\n";
//std::cout << std::get<2>(values2) << "\n";
//std::cout << " A_coord " << std::get<A_coord>(values2) << "\n";
//std::cout << " B_coord " << std::get<B_coord>(values2) << "\n";
//std::cout << " C_coord " << std::get<C_coord>(values2) << "\n";
auto vars3 = vars2.add<D_coord, E_coord, F_coord>(
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
const auto& a = std::get<A_coord>(v);
const auto& b = std::get<B_coord>(v);
const auto& c = std::get<C_coord>(v);
return x * y * z;
},
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
const auto& a = std::get<A_coord>(v);
const auto& b = std::get<B_coord>(v);
const auto& c = std::get<C_coord>(v);
return (x + y + z)/b;
},
[](const auto& v) constexpr {
const auto& x = std::get<X_coord>(v);
const auto& y = std::get<Y_coord>(v);
const auto& z = std::get<Z_coord>(v);
const auto& a = std::get<A_coord>(v);
const auto& b = std::get<B_coord>(v);
const auto& c = std::get<C_coord>(v);
return c/(x * y * z);
});
auto values3 = vars3.ComputeValues(std::make_tuple(X_coord{2.0}, Y_coord{77.0}));
//std::cout << std::get<0>(values3) << "\n";
//std::cout << std::get<1>(values3) << "\n";
//std::cout << std::get<2>(values3) << "\n";
std::cout << std::get<X_coord>(values3) << "\n";
std::cout << std::get<Y_coord>(values3) << "\n";
std::cout << std::get<Z_coord>(values3) << "\n";
std::cout << std::get<A_coord>(values3) << "\n";
std::cout << std::get<B_coord>(values3) << "\n";
std::cout << std::get<C_coord>(values3) << "\n";
std::cout << std::get<D_coord>(values3) << "\n";
std::cout << std::get<E_coord>(values3) << "\n";
std::cout << std::get<F_coord>(values3) << "\n";
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment