Commit 5cff8214 authored by Jan Koniarik's avatar Jan Koniarik
Browse files

Stage I - porting protocol library + formatting

The protocol_item basic thing was ported with additional required types
like bounded, asserts and convert iterator from Schpin.

It is not fortunate that fromatting is in same commit thou
parent e1b740ca
Pipeline #98103 failed with stage
in 49 seconds
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AccessModifierOffset: -8
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: true
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
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
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
AlwaysBreakTemplateDeclarations: true
BasedOnStyle: Google
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: false
# Configure each individual brace in BraceWrapping
BreakBeforeBraces: Custom
BreakStringLiterals: false
# Control of individual brace wrapping cases
BraceWrapping: {
AfterClass: 'true'
AfterControlStatement: 'false'
AfterEnum : 'true'
AfterFunction : 'true'
AfterNamespace : 'true'
AfterStruct : 'true'
AfterUnion : 'true'
BeforeCatch : 'true'
BeforeElse : 'false'
IndentBraces : 'false'
}
MaxEmptyLinesToKeep: 2
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 100
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
DerivePointerBinding: 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
IndentCaseLabels: true
IndentFunctionDeclarationAfterType: false
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
NamespaceIndentation: Inner
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
PenaltyBreakComment: 60
PenaltyBreakFirstLessLess: 1000
PenaltyBreakString: 1
PenaltyExcessCharacter: 1000
PenaltyReturnTypeOnItsOwnLine: 90
PointerAlignment: Left
PointerBindsToType: false
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: true
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesBeforeTrailingComments: 2
SpacesInAngles: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInParentheses: true
SpacesInSquareBrackets: false
Standard: Cpp11
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
Standard: c++20
TabWidth: 4
UseTab: Never
IncludeCategories:
- Regex: '^"'
Priority: 2
- Regex: '^<'
Priority: 3
- Regex: '.\*'
Priority: 1
...
......@@ -20,7 +20,7 @@ build_test:
make -Cbuild -j
exec_test: build_test
cd build && ctest
cd build && ctest --output-on-failure
test: exec_test
......
#include "emlabcpp/algorithm.h"
#include <ctime>
#include <iostream>
#include <list>
......@@ -6,67 +7,69 @@
namespace em = emlabcpp;
int main(int, char *[]) {
std::srand(static_cast<unsigned>(std::time(nullptr)));
int main( int, char*[] )
{
std::srand( static_cast< unsigned >( std::time( nullptr ) ) );
// ---------------------------------------------------------------------------------------
// data structures used in the examples
std::tuple<int, std::string> tpl_data{42, "wololo"};
std::vector<int> vec_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};
std::list<int> list_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};
std::tuple< int, std::string > tpl_data{ 42, "wololo" };
std::vector< int > vec_data{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 };
std::list< int > list_data{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 };
// ---------------------------------------------------------------------------------------
// `for_each(cont, f)` simply executes lambda `f` over each item in container `cont`, the
// crucial property is that container can be either begin/end container or std::tuple-like
// container. This is used to implement algorithms that work over both types of containers.
em::for_each(tpl_data, [&](const auto &item) {
em::for_each( tpl_data, [&]( const auto& item ) {
std::cout << item << '\n';
});
} );
em::for_each(vec_data, [&](int item) {
em::for_each( vec_data, [&]( int item ) {
std::cout << item << '\n';
});
} );
// ---------------------------------------------------------------------------------------
// `find_if(cont,f)` is used to find first item in container `cont` that satisfies predicate
// `f`. This returns index of item for std::tuple-like items and iterator for begin/end
// containers.
std::size_t index = em::find_if(tpl_data, [&](auto item) {
return std::is_same_v<decltype(item), std::string>;
});
std::size_t index = em::find_if( tpl_data, [&]( auto item ) {
return std::is_same_v< decltype( item ), std::string >;
} );
std::cout << "Item with std::string type is at position: " << index << '\n';
auto iter = em::find_if(vec_data, [&](int i) {
auto iter = em::find_if( vec_data, [&]( int i ) {
return i == 0;
});
std::cout << "Zero item is at position: " << std::distance(iter, vec_data.begin()) << '\n';
} );
std::cout << "Zero item is at position: " << std::distance( iter, vec_data.begin() )
<< '\n';
// ---------------------------------------------------------------------------------------
// `ignore(x)` does nothing to the value `x`. Use it to shut down the compiler warning for
// unused variable in situations that requires it.
int unused_variable = 0;
em::ignore(unused_variable);
em::ignore( unused_variable );
// ---------------------------------------------------------------------------------------
// `sign(x)` is used to indicate a sign of variables required for some algorithms. It can be
// used to either explicitly control flow based on the sign or use it to propagate the sign
// into a computation. Note that we consider 0 as a separate case.
int sign_rand_val = (std::rand() % 3) - 1;
switch (em::sign(sign_rand_val)) {
case -1:
std::cout << "sign_rand_val is negative\n";
break;
case 0:
std::cout << "sign_rand_val is zero\n";
break;
case 1:
std::cout << "sign_rand_val is positive\n";
break;
int sign_rand_val = ( std::rand() % 3 ) - 1;
switch ( em::sign( sign_rand_val ) ) {
case -1:
std::cout << "sign_rand_val is negative\n";
break;
case 0:
std::cout << "sign_rand_val is zero\n";
break;
case 1:
std::cout << "sign_rand_val is positive\n";
break;
}
// ---------------------------------------------------------------------------------------
......@@ -74,7 +77,7 @@ int main(int, char *[]) {
// Let's say your light sensor returns values in range 0-255 which actually just represents
// 0-100% intensity.
float mapped = em::map_range(42, 0, 255, 0.0f, 1.0f);
float mapped = em::map_range( 42, 0, 255, 0.0f, 1.0f );
std::cout << "42 mapped from range 0-255 to range 0-1, is: " << mapped << '\n';
// ---------------------------------------------------------------------------------------
......@@ -82,7 +85,7 @@ int main(int, char *[]) {
// situations where equality is not expected or problematic (floats...). `e` specifies
// tolerable range
if (em::almost_equal(0.0f, 0.1f, 0.5)) {
if ( em::almost_equal( 0.0f, 0.1f, 0.5 ) ) {
std::cout << "Yaaaay, 0.1 is within a tolerance of 0.5 to 0.\n";
}
......@@ -90,16 +93,16 @@ int main(int, char *[]) {
// `tail(cont)` returns a view over provided container without the first item, handy for the
// code that handles first item differently.
for (int i : em::tail(vec_data)) {
std::cout << i << '\n'; // note that `1` from the container is ignored
for ( int i : em::tail( vec_data ) ) {
std::cout << i << '\n'; // note that `1` from the container is ignored
}
// ---------------------------------------------------------------------------------------
// `init(cont)` returns a view over provided container without the last item, handy for the
// code that handles last item differently.
for (int i : em::init(vec_data)) {
std::cout << i << '\n'; // note that `0` from the container is ignored
for ( int i : em::init( vec_data ) ) {
std::cout << i << '\n'; // note that `0` from the container is ignored
}
// ---------------------------------------------------------------------------------------
......@@ -109,9 +112,9 @@ int main(int, char *[]) {
//
// There also exists `min_elem` and `max_elem`.
em::min_max<int> mm_res = em::min_max_elem(vec_data, [](int val) {
return em::abs(val);
});
em::min_max< int > mm_res = em::min_max_elem( vec_data, []( int val ) {
return em::abs( val );
} );
std::cout << "Minimal absolute value of vec_data is " << mm_res.min << " and maximum is "
<< mm_res.max << '\n';
......@@ -119,9 +122,9 @@ int main(int, char *[]) {
// `sum(cont, f)` returns sum of result of `f` applied over container `cont`, useful to
// mirror sum in mathematical sense.
int squared_sum = em::sum(vec_data, [](int val) {
int squared_sum = em::sum( vec_data, []( int val ) {
return val * val;
});
} );
std::cout << "Squared sum of values from vector is: " << squared_sum << '\n';
// ---------------------------------------------------------------------------------------
......@@ -129,9 +132,9 @@ int main(int, char *[]) {
// value being 's', this forms `f(f(f(f(s,c[0]),c[1]),c[2])...` chain. Useful for for
// implementing `sum` or doing multiplication instead.
int multiplied_vec = em::accumulate(vec_data, 1, [](int base, int val) {
int multiplied_vec = em::accumulate( vec_data, 1, []( int base, int val ) {
return base * val;
});
} );
std::cout << "Value of all values in vector multiplied together is: " << multiplied_vec
<< '\n';
......@@ -139,28 +142,28 @@ int main(int, char *[]) {
// `avg(cont, f)` can be used to calculate average value of items, with function
// pre-processing them.
float avg_vec = em::avg(vec_data, [&](int val) {
return static_cast<float>(val);
});
float avg_vec = em::avg( vec_data, [&]( int val ) {
return static_cast< float >( val );
} );
std::cout << "Average value of items in vec_data is: " << avg_vec << '\n';
// ---------------------------------------------------------------------------------------
// for_cross_joint(a,b,f) applies `f` to all combinations of values, used for combinational
// stuff and generation of data
em::for_cross_joint(tpl_data, vec_data, [](auto tpl_val, int vec_val) {
em::for_cross_joint( tpl_data, vec_data, []( auto tpl_val, int vec_val ) {
std::cout << "tpl val: " << tpl_val << " vec val: " << vec_val << '\n';
});
} );
// ---------------------------------------------------------------------------------------
// `any_of(cont, f)` returns true if the lambda evaluates at true to at least one item in
// the container. This can be used for checking various properties over container, there is
// also `none_of(cont, f)` and `all_of(cont, f)`
bool has_zero_val = em::any_of(vec_data, [](int val) {
bool has_zero_val = em::any_of( vec_data, []( int val ) {
return val == 0;
});
if (has_zero_val) {
} );
if ( has_zero_val ) {
std::cout << "there is zero in vec_data \\o/ \n";
}
......@@ -168,9 +171,9 @@ int main(int, char *[]) {
// `equal(cont1, cont2)` compares content of containers for equality, this avoids the need
// for same types of containers or values.
bool content_is_equal = em::equal(vec_data, list_data);
bool content_is_equal = em::equal( vec_data, list_data );
std::cout << "The statement that vec_data and list_data has equal content is: "
<< (content_is_equal ? "true" : "false") << '\n';
<< ( content_is_equal ? "true" : "false" ) << '\n';
// ---------------------------------------------------------------------------------------
// `map_f(cont, f)` is used to generate new containers of data based on source container and
......@@ -181,17 +184,17 @@ int main(int, char *[]) {
// This is useful for situations like: for each item in dataset calculate X and store it in
// a new container.
auto mapped_vec = em::map_f<std::vector<int>>(vec_data, [](int val) {
auto mapped_vec = em::map_f< std::vector< int > >( vec_data, []( int val ) {
return -val;
});
for (int v : mapped_vec) {
} );
for ( int v : mapped_vec ) {
std::cout << "mapped val: " << v << '\n';
}
std::array<std::string, 11> mapped_arr = em::map_f_to_a<11>(vec_data, [](auto item) {
return std::to_string(item);
});
for (const std::string &s : mapped_arr) {
std::array< std::string, 11 > mapped_arr = em::map_f_to_a< 11 >( vec_data, []( auto item ) {
return std::to_string( item );
} );
for ( const std::string& s : mapped_arr ) {
std::cout << "mapped arr val: " << s << '\n';
}
......@@ -199,21 +202,23 @@ int main(int, char *[]) {
// as a shortcut for `map_f` functions, we have `convert_to<T>` and it's `T operator(U)`
// which simply constructs T from inserted types.
struct foo_conv {
struct foo_conv
{
int i;
};
auto mapped_struct_vec =
em::map_f<std::vector<foo_conv>>(vec_data, em::convert_to<foo_conv>{});
em::map_f< std::vector< foo_conv > >( vec_data, em::convert_to< foo_conv >{} );
for (foo_conv v : mapped_struct_vec) {
for ( foo_conv v : mapped_struct_vec ) {
std::cout << "mapped float val: " << v.i << '\n';
}
// ---------------------------------------------------------------------------------------
// `joined` is used for example to join strings with an delimiter
std::vector<std::string> string_data{"a", "b", "c", "d", "e"};
std::vector< std::string > string_data{ "a", "b", "c", "d", "e" };
std::cout << "joined string data: " << em::joined(string_data, std::string{","}) << '\n';
std::cout << "joined string data: " << em::joined( string_data, std::string{ "," } )
<< '\n';
}
This diff is collapsed.
#include "emlabcpp/types.h"
#include <tuple>
#pragma once
namespace emlabcpp::impl {
namespace emlabcpp::impl
{
template <typename... Args, typename UnaryFunction, std::size_t... Idx>
[[nodiscard]] constexpr std::size_t find_if_impl(const std::tuple<Args...> &t, UnaryFunction &&f,
std::index_sequence<Idx...>) {
std::size_t res = sizeof...(Args);
auto ff = [&](const auto &item, std::size_t i) {
if (f(item)) {
template < typename... Args, typename UnaryFunction, std::size_t... Idx >
[[nodiscard]] constexpr std::size_t
find_if_impl( const std::tuple< Args... >& t, UnaryFunction&& f, std::index_sequence< Idx... > )
{
std::size_t res = sizeof...( Args );
auto ff = [&]( const auto& item, std::size_t i ) {
if ( f( item ) ) {
res = i;
return true;
}
return false;
};
(ff(std::get<Idx>(t), Idx) || ...);
( ff( std::get< Idx >( t ), Idx ) || ... );
return res;
}
template <typename T, std::size_t N, typename Container, typename UnaryFunction, std::size_t... Is>
[[nodiscard]] inline std::array<T, N> map_f_to_a_impl(Container &&cont, UnaryFunction &&f,
std::integer_sequence<std::size_t, Is...>) {
template < typename T, std::size_t N, typename Container, typename UnaryFunction, std::size_t... Is >
[[nodiscard]] inline std::array< T, N >
map_f_to_a_impl( Container&& cont, UnaryFunction&& f, std::integer_sequence< std::size_t, Is... > )
{
auto iter = cont.begin();
auto process = [&](auto) {
if constexpr (std::is_reference_v<Container>) {
return f(*iter++);
auto process = [&]( auto ) {
if constexpr ( std::is_reference_v< Container > ) {
return f( *iter++ );
} else {
return f(std::move(*iter++));
return f( std::move( *iter++ ) );
}
};
// https://en.cppreference.com/w/cpp/language/eval_order
// based on standard the order of process(i) calls is defined only in case we are using
// constructor initializer with {} brackets. Otherwise it can be any order ...
return std::array<T, N>{process(Is)...};
return std::array< T, N >{ process( Is )... };
}
template <typename>
template < typename >
struct map_f_collector;
template <typename T>
requires requires(T a, typename T::value_type b) {
a.push_back(b);
template < typename T >
requires requires( T a, typename T::value_type b )
{
a.push_back( b );
}
struct map_f_collector<T> {
void collect(T &res, typename T::value_type val) { res.push_back(std::move(val)); }
struct map_f_collector< T >
{
void collect( T& res, typename T::value_type val )
{
res.push_back( std::move( val ) );
}
};
template <typename T>
requires requires(T a, typename T::value_type b) {
a.insert(b);
template < typename T >
requires requires( T a, typename T::value_type b )
{
a.insert( b );
}
struct map_f_collector<T> {
void collect(T &res, typename T::value_type val) { res.insert(std::move(val)); }
struct map_f_collector< T >
{
void collect( T& res, typename T::value_type val )
{
res.insert( std::move( val ) );
}
};
template <typename T, std::size_t N>
struct map_f_collector<std::array<T, N>> {
template < typename T, std::size_t N >
struct map_f_collector< std::array< T, N > >
{
std::size_t i = 0;
void collect(std::array<T, N> &res, T val) {
res[i] = std::move(val);
void collect( std::array< T, N >& res, T val )
{
res[i] = std::move( val );
i += 1;
}
};
template <typename T>
concept map_f_collectable = requires(T item, typename T::value_type val) {
map_f_collector<T>{}.collect(item, val);
template < typename T >
concept map_f_collectable = requires( T item, typename T::value_type val )
{
map_f_collector< T >{}.collect( item, val );
};
} // namespace emlabcpp::impl
} // namespace emlabcpp::impl
#include <cassert>
#define EMLABCPP_ASSERT( cond ) assert( cond )