initial commit

main
keldu 2022-07-31 19:20:50 +02:00
commit 405827519b
8 changed files with 372 additions and 0 deletions

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Typesafe C++ Unit library
Allows the declaration of typesafe units and their usage.
Supported operations are '+', '-', '*', '/'.
Conversion and use between different base types currently are not available.

View File

@ -0,0 +1,20 @@
#pragma once
#include "../unit.h"
#include <string_view>
namespace kelun {
namespace unit_type {
struct meter{
static constexpr std::string_view name = "meter";
static constexpr std::string_view short_name = "m";
};
}
template<typename S>
using meter = unit<S,unit_base<unit_type::meter,1>>;
template<typename S>
using square_meter = unit<S,unit_base<unit_type::meter,2>>;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "../unit.h"
namespace kelun {
namespace unit_type {
struct second{
static constexpr std::string_view name = "second";
static constexpr std::string_view short_name = "s";
};
}
template<typename S>
using second = unit<S,unit_base<unit_type::second,1>>;
}

49
source/kelunit/unit.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include "./unit_reduction.h"
namespace kelun {
template<typename UnitType, int64_t Exponent>
struct unit_base {};
template<typename StorageT, typename... T>
class unit;
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
class unit<StorageT, unit_base<UnitTypes, Exponents>...> {
public:
using value_type = StorageT;
unit() = default;
unit(const unit<StorageT,unit_base<UnitTypes, Exponents>...>&) = default;
unit(unit<StorageT,unit_base<UnitTypes,Exponents>...>&&) = default;
unit<StorageT,unit_base<UnitTypes,Exponents>...>& operator=(const unit<StorageT,unit_base<UnitTypes,Exponents>...>&) = default;
unit<StorageT,unit_base<UnitTypes,Exponents>...>& operator=(unit<StorageT,unit_base<UnitTypes,Exponents>...>&&) = default;
unit(const value_type&);
unit(value_type&&);
unit<StorageT,unit_base<UnitTypes,Exponents>...>& operator=(const value_type&);
unit<StorageT,unit_base<UnitTypes,Exponents>...>& operator=(value_type&&);
unit<StorageT,unit_base<UnitTypes,Exponents>...> operator+(const unit<StorageT,unit_base<UnitTypes,Exponents>...>& rhs);
unit<StorageT,unit_base<UnitTypes,Exponents>...> operator-(const unit<StorageT,unit_base<UnitTypes,Exponents>...>& rhs);
template<typename... Trhs>
typename unit_multiplication<unit<StorageT,unit_base<UnitTypes,Exponents>...>, unit<StorageT,Trhs...>>::type operator*(const unit<StorageT, Trhs...>& rhs);
template<typename... Trhs>
typename unit_multiplication<unit<StorageT, unit_base<UnitTypes,Exponents>...>, typename unit_invert<StorageT,Trhs...>::type>::type operator/(const unit<StorageT, Trhs...>& rhs);
value_type data() const;
private:
value_type value;
};
template<typename S>
using scalar = unit<S>;
}
#include "unit.tmpl.h"

View File

@ -0,0 +1,53 @@
#include <utility>
namespace kelun {
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...>::unit(const value_type& value_):
value{value_}{}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...>::unit(value_type&& value_):value{std::move(value_)}{}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...>& unit<StorageT,unit_base<UnitTypes, Exponents>...>::operator=(const typename unit<StorageT,unit_base<UnitTypes, Exponents>...>::value_type& value_){
value = value_;
return *this;
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...>& unit<StorageT,unit_base<UnitTypes, Exponents>...>::operator=(typename unit<StorageT,unit_base<UnitTypes, Exponents>...>::value_type&& value_){
value = std::move(value_);
return *this;
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...> unit<StorageT,unit_base<UnitTypes, Exponents>...>::operator+(const unit<StorageT,unit_base<UnitTypes, Exponents>...>& rhs){
return value + rhs.value;
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
unit<StorageT,unit_base<UnitTypes, Exponents>...> unit<StorageT,unit_base<UnitTypes, Exponents>...>::operator-(const unit<StorageT,unit_base<UnitTypes, Exponents>...>& rhs){
return value - rhs.value;
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
template<typename... Trhs>
typename unit_multiplication<unit<StorageT,unit_base<UnitTypes,Exponents>...>, unit<StorageT, Trhs...>>::type
unit<StorageT,unit_base<UnitTypes, Exponents>...>::operator*(const unit<StorageT,Trhs...>& rhs){
return value * rhs.data();
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
template<typename... Trhs>
typename unit_multiplication<unit<StorageT,unit_base<UnitTypes,Exponents>...>, typename unit_invert<StorageT,Trhs...>::type>::type
unit<StorageT,unit_base<UnitTypes,Exponents>...>::operator/(const unit<StorageT, Trhs...>& rhs){
typename unit_invert<StorageT, Trhs...>::type rhs_inverted{static_cast<StorageT>(1)/rhs.data()};
return value * rhs_inverted.data();
}
template<typename StorageT, typename... UnitTypes, int64_t... Exponents>
typename unit<StorageT,unit_base<UnitTypes, Exponents>...>::value_type unit<StorageT,unit_base<UnitTypes, Exponents>...>::data() const {
return value;
}
}

View File

@ -0,0 +1,55 @@
#include "./unit.h"
#include <iostream>
namespace std {
template<typename UnitT, int64_t UnitE>
inline ostream& operator<<(ostream& o, const kelun::unit_base<UnitT,UnitE>& ele){
o<<UnitT::short_name;
if constexpr ( UnitE != 1 ){
o<<'^'<<'('<<UnitE<<')';
}
return o;
}
}
namespace kelun {
template<typename... T>
struct unit_print_impl {
static_assert(is_always_false<T...>, "Template type not supported");
};
template<typename UnitT, int64_t UnitE, typename... UnitTL, int64_t... UnitEL>
struct unit_print_impl<unit_base<UnitT,UnitE>,unit_base<UnitTL,UnitEL>...> {
static std::ostream& print(std::ostream& o){
unit_base<UnitT,UnitE> element;
std::ostream& o_ret = o << element;
if constexpr (sizeof...(UnitTL) > 0){
std::ostream& o_ret_next = o_ret << ' ' << '*' << ' ';
return unit_print_impl<unit_base<UnitTL,UnitEL>...>::print(o_ret_next);
}
return o_ret<<']';
}
};
}
namespace std {
template<typename StorageT, typename... T>
inline ostream& operator<<(ostream& o, const kelun::unit<StorageT,T...>& unit);
template<typename StorageT, typename... UnitT, int64_t... UnitE>
inline ostream& operator<<(ostream& o, const kelun::unit<StorageT,kelun::unit_base<UnitT,UnitE>...>& unit){
o << unit.data();
if constexpr (sizeof...(UnitT) > 0) {
auto& o_ret = o << ' '<<'[';
return kelun::unit_print_impl<kelun::unit_base<UnitT,UnitE>...>::print(o_ret);
}
return o;
}
}

View File

@ -0,0 +1,133 @@
#pragma once
#include <cstdint>
#include <type_traits>
namespace kelun {
template<typename... T>
constexpr bool is_always_false = false;
template<typename UnitType, int64_t Exponent>
struct unit_base;
template<typename StorageT, typename... T>
class unit;
namespace impl {
template<typename T, typename U>
class unit_matching;
template<typename... T>
class unit_redux_list {
static_assert(sizeof...(T) == 0, "Template type not supported");
using reduced_typed = unit_redux_list<>;
};
template<typename T0, int64_t E0, typename... TL, int64_t... EL>
struct unit_redux_list<unit_base<T0,E0>, unit_base<TL,EL>...> {
using reduced_type = typename unit_matching<unit_redux_list<unit_base<T0,E0>, unit_base<TL,EL>...>, unit_redux_list<>>::type;
};
template<typename T, typename U, typename V>
class unit_matching_reduce {
public:
static_assert(is_always_false<T, U, V>, "Template type not supported");
};
template<typename T, int64_t E, typename T0, int64_t E0, typename... TL, int64_t... EL, typename... TR, int64_t... ER>
class unit_matching_reduce<unit_base<T,E>, unit_redux_list<unit_base<T0,E0>,unit_base<TL,EL>...>, unit_redux_list<unit_base<TR,ER>...>> {
public:
static constexpr bool is_same = std::is_same_v<T,T0>;
using match_reduce_type = typename std::conditional<is_same, unit_base<T,E+E0>, unit_base<T,E>>::type;
using match_reduce_unit_redux_list = typename std::conditional<is_same, unit_redux_list<unit_base<TR,ER>...>, unit_redux_list<unit_base<TR,ER>..., unit_base<T0,E0>>>::type;
using value_type = typename unit_matching_reduce<match_reduce_type, unit_redux_list<unit_base<TL,EL>...>, match_reduce_unit_redux_list>::value_type;
using unit_redux_list_type = typename unit_matching_reduce<match_reduce_type, unit_redux_list<unit_base<TL,EL>...>, match_reduce_unit_redux_list>::unit_redux_list_type;
static constexpr int64_t value_num = unit_matching_reduce<match_reduce_type, unit_redux_list<unit_base<TL,EL>...>, match_reduce_unit_redux_list>::value_num;
};
template<typename T, int64_t E, typename... TR, int64_t... ER>
class unit_matching_reduce<unit_base<T,E>, unit_redux_list<>, unit_redux_list<unit_base<TR,ER>...>> {
public:
using value_type = unit_base<T,E>;
using unit_redux_list_type = unit_redux_list<unit_base<TR,ER>...>;
static constexpr int64_t value_num = E;
};
template<typename T, typename U>
class unit_matching {
static_assert(is_always_false<T, U>, "Template type not supported");
};
template<typename... T, int64_t... E>
class unit_matching<unit_redux_list<>,unit_redux_list<unit_base<T,E>...>> {
public:
using type = unit_redux_list<unit_base<T,E>...>;
};
template<typename T0, int64_t E0, typename... TL, int64_t... EL, typename... TR, int64_t... ER>
class unit_matching<unit_redux_list<unit_base<T0,E0>,unit_base<TL,EL>...>, unit_redux_list<unit_base<TR,ER>...>> {
public:
using reduced_value_type = typename unit_matching_reduce<unit_base<T0,E0>, unit_redux_list<unit_base<TL,EL>...>, unit_redux_list<>>::value_type;
using reduced_unit_redux_list_type = typename unit_matching_reduce<unit_base<T0,E0>, unit_redux_list<unit_base<TL,EL>...>, unit_redux_list<>>::unit_redux_list_type;
static constexpr int64_t reduced_value_num = unit_matching_reduce<unit_base<T0,E0>, unit_redux_list<unit_base<TL,EL>...>, unit_redux_list<>>::value_num;
using reduced_result_unit_redux_list = typename std::conditional<reduced_value_num == 0, unit_redux_list<unit_base<TR,ER>...>, unit_redux_list<unit_base<TR,ER>...,reduced_value_type>>::type;
using type = typename unit_matching<reduced_unit_redux_list_type, reduced_result_unit_redux_list>::type;
};
template<typename List, typename StorageT>
class unit_matching_add_storage {
public:
static_assert(is_always_false<List,StorageT>, "Template type not supported");
};
template<typename StorageT, typename... UnitTypes, int64_t... UnitExponents>
class unit_matching_add_storage<unit_redux_list<unit_base<UnitTypes,UnitExponents>...>, StorageT> {
public:
using type = unit<StorageT, unit_base<UnitTypes,UnitExponents>...>;
};
}
template<typename StorageT, typename... T>
class unit_reduction {
static_assert(is_always_false<StorageT,T...>, "Template type not supported");
};
template<typename StorageT, typename... UnitT, int64_t... UnitE>
class unit_reduction<StorageT, unit_base<UnitT, UnitE>...> {
public:
using list_type = typename impl::unit_matching<impl::unit_redux_list<unit_base<UnitT, UnitE>...>, impl::unit_redux_list<>>::type;
using type = typename impl::unit_matching_add_storage<list_type, StorageT>::type;
};
template<typename StorageT, typename... T>
class unit_invert {
static_assert(is_always_false<StorageT, T...>, "Template type not supported");
};
template<typename StorageT, typename... UnitT, int64_t... UnitE>
class unit_invert<StorageT, unit_base<UnitT, UnitE>...> {
public:
using type = unit<StorageT, unit_base<UnitT, -UnitE>...>;
};
template<typename T, typename U>
class unit_multiplication{
static_assert(is_always_false<T, U>, "Template type not supported");
};
template<typename StorageT, typename... UnitT, int64_t... UnitE, typename... UnitRhsT, int64_t... UnitRhsE>
class unit_multiplication<unit<StorageT, unit_base<UnitT,UnitE>...>, unit<StorageT, unit_base<UnitRhsT, UnitRhsE>...>> {
public:
using type = typename unit_reduction<StorageT, unit_base<UnitT,UnitE>..., unit_base<UnitRhsT, UnitRhsE>...>::type;
};
}

40
test/basic.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "../source/kelunit/distance/meter.h"
#include "../source/kelunit/time/second.h"
#include "../source/kelunit/unit_print.h"
#include <iostream>
int main(){
using scalar = kelun::scalar<float>;
using meter = kelun::meter<float>;
using square_meter = kelun::square_meter<float>;
using second = kelun::second<float>;
meter a = 6.f;
meter b = 3.f;
second s = 10.f;
meter c = a+b;
meter d = a-b;
square_meter e = a*b;
//
scalar f = a/b;
auto mps = b / s;
std::cout<<"Values:\n";
std::cout<<"a: "<<a<<"\n";
std::cout<<"b: "<<b<<"\n";
std::cout<<"c: "<<c<<"\n";
std::cout<<"d: "<<d<<"\n";
std::cout<<"e: "<<e<<"\n";
std::cout<<"f: "<<f<<"\n";
std::cout<<"mps: "<<mps<<"\n";
std::cout<<std::endl;
return 0;
}