├── README.md ├── src ├── playground.cpp ├── thrift_service.thrift ├── protobuf.proto ├── grpc_service.proto ├── trace.hpp ├── c_api_lambda.cpp ├── plugin_lib.cpp ├── module_1.cpp ├── module_2.cpp ├── uuid.cpp ├── modules.cpp ├── utils.cpp ├── benchmark-hayai.cpp ├── benchmark-nonius.cpp ├── sso.cpp ├── pragma.cpp ├── benchmark-catch.cpp ├── I.hpp ├── multi_hash.cpp ├── benchmark-google.cpp ├── stacktrace.cpp ├── protobuf.cpp ├── xmlrpc_c.cpp ├── benchmark-celero.cpp ├── newtrace.cpp ├── templates.cpp ├── enum.cpp ├── interview_4.cpp ├── lrpc_proto.hpp ├── interview_1.cpp ├── attributes.cpp ├── exceptions.cpp ├── dekker_n.cpp ├── queue.cpp ├── xmlrpc_s.cpp ├── round.cpp ├── adapter.cpp ├── dekker.cpp ├── plugin_load.cpp ├── constructors.cpp ├── inverting.cpp ├── assignment.cpp ├── cache.cpp ├── random_test.cpp ├── ranges.cpp ├── interview_5.cpp ├── bloom.cpp ├── thrift_service_c.cpp ├── grpc_service_s.cpp ├── pstl_test_gcc.cpp ├── atomics.cpp ├── cpu_timer.cpp ├── pstl_test.cpp ├── try_block.cpp ├── newtrace.st.cpp ├── interview_2.cpp ├── deep_ptr.cpp ├── memory_pool.cpp ├── enum2.cpp ├── delete.cpp ├── alignas.cpp ├── thrift_service_s.cpp ├── compression.cpp ├── otp.cpp ├── grpc_service_c.cpp ├── bad_pointer.cpp ├── file_hash.cpp ├── membars.cpp ├── echo_c.cpp ├── hash.hpp ├── geoip.cpp ├── echo_s.cpp ├── synchronized.cpp ├── new.cpp ├── timer_test.cpp ├── options.cpp ├── xml.cpp ├── throttle.cpp ├── deadlock.cpp ├── interview_6.cpp ├── newtrace.txt ├── sorting.cpp ├── bloom.hpp ├── hash.cpp ├── unordered.cpp ├── colors.cpp ├── utils.hpp ├── semaphore.hpp ├── fmt.cpp ├── base64.cpp ├── database.cpp ├── visitor.cpp ├── handle.hpp ├── istring.cpp ├── lesson_pool_allocator.cpp ├── event.hpp ├── singleton.cpp ├── newtrace.st.hpp ├── lesson_pool_allocator_benchmark.cpp ├── lesson_thread_pool_how_to.cpp ├── singleton.hpp ├── aos_soa.cpp ├── unique.cpp ├── lrpc_s.cpp ├── erased.cpp ├── pool.cpp ├── lesson_memory_pool_how_to.cpp ├── base64.hpp ├── deep_ptr.hpp ├── mutex.hpp ├── lesson_spinlock_event_semaphore_how_to.cpp ├── class.cpp ├── round.hpp ├── synchronized.hpp ├── crypto.cpp ├── istring.hpp └── lrpc_c.cpp ├── run_clion.sh ├── run_gcc.sh ├── run_llvm.sh ├── run_xcode.sh ├── env_llvm.sh ├── env_gcc.sh ├── LICENSE ├── run_msvc.bat ├── .gitignore ├── cmake ├── FindCURLpp.cmake ├── Findlz4.cmake ├── FindBenchmark.cmake ├── FindBotan.cmake ├── FindWolfSSL.cmake ├── FindGRPC.cmake ├── FindSOCI.cmake ├── FindThrift.cmake └── FindMYSQL.cmake ├── run_jenkins.sh ├── CMakeLists.txt.msvc ├── CMakeLists.txt.llvm └── CMakeLists.txt.gcc /README.md: -------------------------------------------------------------------------------- 1 | Code samples from https://vorbrodt.blog 2 | -------------------------------------------------------------------------------- /src/playground.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /run_clion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ~/Code/blog 4 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 5 | 6 | cd src 7 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 8 | cd .. 9 | -------------------------------------------------------------------------------- /src/thrift_service.thrift: -------------------------------------------------------------------------------- 1 | namespace cpp ThriftService 2 | 3 | service ThriftService 4 | { 5 | void ping(), 6 | void hello(1:string msg), 7 | oneway void async_call() 8 | } 9 | -------------------------------------------------------------------------------- /src/protobuf.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package data; 4 | 5 | message Person 6 | { 7 | required string name = 1; 8 | required int32 dob = 2; 9 | optional string email = 3; 10 | } 11 | -------------------------------------------------------------------------------- /src/grpc_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Input 4 | { 5 | string input_msg = 1; 6 | } 7 | 8 | message Output 9 | { 10 | string output_msg = 1; 11 | } 12 | 13 | service gRPCService 14 | { 15 | rpc hello(Input) returns (Output) {} 16 | } 17 | -------------------------------------------------------------------------------- /src/trace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace { static inline std::mutex kStdOutLock; } 7 | 8 | template 9 | inline void trace(Ts&&... args) 10 | { 11 | std::scoped_lock lock(kStdOutLock); 12 | (std::cout << ... << args) << std::endl; 13 | } 14 | -------------------------------------------------------------------------------- /run_gcc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ~/Code/blog 4 | ln -sf CMakeLists.txt.gcc CMakeLists.txt 5 | cd src 6 | ln -sf CMakeLists.txt.gcc CMakeLists.txt 7 | cd .. 8 | 9 | source env_gcc.sh 10 | 11 | rm -rf gcc 2> /dev/null 12 | mkdir gcc 2> /dev/null 13 | cd gcc 14 | cmake -DCMAKE_OSX_SYSROOT="$(xcrun --show-sdk-path)" .. 15 | -------------------------------------------------------------------------------- /run_llvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ~/Code/blog 4 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 5 | cd src 6 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 7 | cd .. 8 | 9 | source env_llvm.sh 10 | 11 | rm -rf llvm 2> /dev/null 12 | mkdir llvm 2> /dev/null 13 | cd llvm 14 | cmake -DCMAKE_OSX_SYSROOT="$(xcrun --show-sdk-path)" .. 15 | -------------------------------------------------------------------------------- /run_xcode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ~/Code/blog 4 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 5 | 6 | cd src 7 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 8 | cd .. 9 | 10 | mkdir xcode 2> /dev/null 11 | cd xcode 12 | rm CMakeCache.txt 2> /dev/null 13 | cmake -DCMAKE_OSX_SYSROOT="$(xcrun --show-sdk-path)" -G Xcode .. 14 | cd .. 15 | -------------------------------------------------------------------------------- /src/c_api_lambda.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | typedef void(*FuncPtr)(int arg); 6 | FuncPtr callback; 7 | 8 | void set_callback(FuncPtr fp) { callback = fp; } 9 | void fire_callback(int arg) { callback(arg); } 10 | 11 | int main() 12 | { 13 | set_callback(+[](int arg) { cout << arg << endl; }); 14 | fire_callback(42); 15 | } 16 | -------------------------------------------------------------------------------- /src/plugin_lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace plugin 4 | { 5 | const char* plugin_name() 6 | { 7 | return "Vorbrodt's 1st Plugin"; 8 | } 9 | 10 | const char* plugin_version() 11 | { 12 | return "1.0"; 13 | } 14 | } 15 | 16 | BOOST_DLL_ALIAS(plugin::plugin_name, plugin_name); 17 | BOOST_DLL_ALIAS(plugin::plugin_version, plugin_version); 18 | -------------------------------------------------------------------------------- /src/module_1.cpp: -------------------------------------------------------------------------------- 1 | export module module_1; 2 | 3 | export auto global_module_1_name = "module_1"; 4 | 5 | export auto module_1_name() { return ::global_module_1_name; } 6 | 7 | export namespace module_1 8 | { 9 | auto name() { return ::module_1_name(); } 10 | 11 | struct S 12 | { 13 | auto name() { return ::module_1::name(); } 14 | }; 15 | } 16 | 17 | module :private; 18 | -------------------------------------------------------------------------------- /src/module_2.cpp: -------------------------------------------------------------------------------- 1 | export module module_2; 2 | 3 | export auto global_module_2_name = "module_2"; 4 | 5 | export auto module_2_name() { return ::global_module_2_name; } 6 | 7 | export namespace module_2 8 | { 9 | auto name() { return ::module_2_name(); } 10 | 11 | struct S 12 | { 13 | auto name() { return ::module_2::name(); } 14 | }; 15 | } 16 | 17 | module :private; 18 | -------------------------------------------------------------------------------- /src/uuid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace boost::uuids; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | auto gen = random_generator(); 12 | uuid id = gen(); 13 | 14 | cout << id << endl; 15 | 16 | return 1; 17 | } 18 | -------------------------------------------------------------------------------- /src/modules.cpp: -------------------------------------------------------------------------------- 1 | import ; 2 | import module_1; 3 | import module_2; 4 | 5 | int main() 6 | { 7 | using namespace std; 8 | 9 | cout << global_module_1_name << ", " << module_1_name() << ", " << module_1::name() << ", " << module_1::S().name() << endl; 10 | cout << global_module_2_name << ", " << module_2_name() << ", " << module_2::name() << ", " << module_2::S().name() << endl; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "I.hpp" 2 | #include "T.hpp" 3 | #include "utils.hpp" 4 | 5 | using namespace std; 6 | using namespace std::placeholders; 7 | 8 | int main() 9 | { 10 | stack_timer timer; 11 | 12 | auto v = make_vector(5, "C++"); 13 | 14 | auto b3 = bind(&T::foo, _1); 15 | for_each(begin(v), end(v), b3); 16 | 17 | auto b4 = mem_fn(&T::bar); 18 | for_each(begin(v), end(v), b4); 19 | } 20 | -------------------------------------------------------------------------------- /src/benchmark-hayai.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | string short_string = "hello"; 7 | string long_string("0123456789abcdefghijklmnopqrstuvwxyz"); 8 | 9 | BENCHMARK(StringOps, StringCopyShort, 100, 1'000'000) 10 | { 11 | string copy(short_string); 12 | } 13 | 14 | BENCHMARK(StringOps, StringCopyLong, 100, 100'000) 15 | { 16 | string copy(long_string); 17 | } 18 | -------------------------------------------------------------------------------- /src/benchmark-nonius.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define NONIUS_RUNNER 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | string short_string = "hello"; 9 | string long_string("0123456789abcdefghijklmnopqrstuvwxyz"); 10 | 11 | NONIUS_BENCHMARK("StringCopyShort", [] 12 | { 13 | string copy(short_string); 14 | }) 15 | 16 | NONIUS_BENCHMARK("StringCopyLong", [] 17 | { 18 | string copy(long_string); 19 | }) 20 | -------------------------------------------------------------------------------- /src/sso.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | auto size = sizeof(string); 9 | auto capacity = string().capacity(); 10 | auto small = string(capacity, '*'); 11 | auto big = string(capacity + 1, '*'); 12 | 13 | cout << "sizeof : " << size << endl; 14 | cout << "Capacity: " << capacity << endl; 15 | cout << "Small : " << small.capacity() << endl; 16 | cout << "Big : " << big.capacity() << endl; 17 | } 18 | -------------------------------------------------------------------------------- /src/pragma.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | struct S1 6 | { 7 | char c; 8 | short s; 9 | int i; 10 | long l; 11 | float f; 12 | double d; 13 | }; 14 | 15 | #pragma pack(show) 16 | #pragma pack(push, 1) 17 | #pragma pack(show) 18 | 19 | struct S2 20 | { 21 | char c; 22 | short s; 23 | int i; 24 | long l; 25 | float f; 26 | double d; 27 | }; 28 | 29 | #pragma pack(pop) 30 | 31 | int main() 32 | { 33 | cout << sizeof(S1) << ", " << sizeof(S2) << endl; 34 | } 35 | -------------------------------------------------------------------------------- /src/benchmark-catch.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | string short_string = "hello"; 9 | string long_string("0123456789abcdefghijklmnopqrstuvwxyz"); 10 | 11 | TEST_CASE("string ops", "[benchmark]") 12 | { 13 | BENCHMARK("StringCopyShort") 14 | { 15 | string copy(short_string); 16 | }; 17 | 18 | BENCHMARK("StringCopyLong") 19 | { 20 | string copy(long_string); 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/I.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | -------------------------------------------------------------------------------- /env_llvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export CC="/usr/local/bin/clang" 4 | export CXX="/usr/local/bin/clang++" 5 | export LD="/usr/local/bin/ld64.lld" 6 | export AR="/usr/local/bin/llvm-ar" 7 | export RANLIB="/usr/local/bin/llvm-ranlib" 8 | 9 | export CPPFLAGS="-I$(xcrun --show-sdk-path)/usr/include -I/usr/local/include/c++/v1 -I/usr/local/include" 10 | export CXXFLAGS="$CPPFLAGS" 11 | export LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib" 12 | 13 | alias cc=$CC 14 | alias c++=$CXX 15 | alias ld=$LD 16 | alias ar=$AR 17 | alias ranlib=$RANLIB 18 | -------------------------------------------------------------------------------- /src/multi_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "hash.hpp" 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | auto s = "Hash from this string is..."s; 10 | 11 | auto h = hashN(s, 3); 12 | 13 | cout << "hashN(\"" << s << "\", 3);" << endl; 14 | for(auto it : h) cout << (std::size_t)it << " "; 15 | cout << endl << endl; 16 | 17 | auto [h1, h2, h3] = hashN<3>(s); 18 | 19 | cout << "hashN<3>(\"" << s << "\");" << endl; 20 | cout << (std::size_t)h1 << " " << (std::size_t)h2 << " " << (std::size_t)h3 << endl << endl; 21 | } 22 | -------------------------------------------------------------------------------- /src/benchmark-google.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | string short_string = "hello"; 7 | string long_string("0123456789abcdefghijklmnopqrstuvwxyz"); 8 | 9 | void BM_StringCopyShort(benchmark::State& state) 10 | { 11 | for (auto _ : state) 12 | string copy(short_string); 13 | } 14 | BENCHMARK(BM_StringCopyShort); 15 | 16 | void BM_StringCopyLong(benchmark::State& state) 17 | { 18 | for (auto _ : state) 19 | string copy(long_string); 20 | } 21 | BENCHMARK(BM_StringCopyLong); 22 | 23 | BENCHMARK_MAIN(); 24 | -------------------------------------------------------------------------------- /src/stacktrace.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED 4 | #include 5 | 6 | using namespace std; 7 | using namespace boost::stacktrace; 8 | 9 | void f1(); 10 | void f2(); 11 | void f3(); 12 | 13 | void f1() { f2(); } 14 | void f2() { f3(); } 15 | void f3() { cout << stacktrace() << endl << endl; } 16 | 17 | int main(int argc, char** argv) 18 | { 19 | f1(); 20 | 21 | auto f = async(launch::async, []() { cout << stacktrace() << endl << endl; }); 22 | f.get(); 23 | 24 | return 1; 25 | } 26 | -------------------------------------------------------------------------------- /env_gcc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export CC="/usr/local/bin/gcc-12" 4 | export CXX="/usr/local/bin/gcc-12" 5 | export LD="/usr/local/bin/ld64.lld" 6 | export AR="/usr/local/bin/gcc-ar-12" 7 | export RANLIB="/usr/local/bin/gcc-ranlib-12" 8 | 9 | export CPPFLAGS="-I$(xcrun --show-sdk-path)/usr/include -I/usr/local/Cellar/gcc/12.2.0/include/c++/12 -I/usr/local/include" 10 | export CXXFLAGS="$CPPFLAGS" 11 | export LDFLAGS="-L/usr/local/Cellar/gcc/12.2.0/lib/gcc/12 -L/usr/local/lib -Wl,-rpath,/usr/local/lib" 12 | 13 | alias cc=$CC 14 | alias c++=$CXX 15 | alias ld=$LD 16 | alias ar=$AR 17 | alias ranlib=$RANLIB 18 | -------------------------------------------------------------------------------- /src/protobuf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "protobuf.pb.h" 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | GOOGLE_PROTOBUF_VERIFY_VERSION; 10 | 11 | data::Person p; 12 | 13 | p.set_name("Martin Vorbrodt"); 14 | p.set_dob(19800830); 15 | p.set_email("martin@vorbrodt.blog"); 16 | 17 | string binary; 18 | p.SerializeToString(&binary); 19 | 20 | data::Person p2; 21 | p2.ParseFromString(binary); 22 | 23 | cout << "Name = " << p2.name() << endl; 24 | cout << "DOB = " << p2.dob() << endl; 25 | cout << "EMail = " << p2.email() << endl; 26 | 27 | google::protobuf::ShutdownProtobufLibrary(); 28 | } 29 | -------------------------------------------------------------------------------- /src/xmlrpc_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | string serverUrl("http://localhost:8080/RPC2"); 12 | string methodName("hello"); 13 | 14 | xmlrpc_c::clientSimple client; 15 | xmlrpc_c::value result; 16 | 17 | client.call(serverUrl, methodName, "s", &result, "XMLRPC client says hello!"); 18 | 19 | xmlrpc_c::value_string reply = xmlrpc_c::value_string(result); 20 | 21 | cout << static_cast(reply) << endl; 22 | 23 | return 1; 24 | } 25 | -------------------------------------------------------------------------------- /src/benchmark-celero.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | string short_string = "hello"; 7 | string long_string("0123456789abcdefghijklmnopqrstuvwxyz"); 8 | 9 | BASELINE(StringCopyShort, Baseline10, 100, 100'000) 10 | { 11 | string copy(short_string); 12 | } 13 | 14 | BENCHMARK(StringCopyShort, Baseline1000, 100, 1'000'000) 15 | { 16 | string copy(short_string); 17 | } 18 | 19 | BASELINE(StringCopyLong, Baseline10, 100, 100'000) 20 | { 21 | string copy(long_string); 22 | } 23 | 24 | BENCHMARK(StringCopyLong, Baseline1000, 100, 1'000'000) 25 | { 26 | string copy(long_string); 27 | } 28 | 29 | CELERO_MAIN; 30 | -------------------------------------------------------------------------------- /src/newtrace.cpp: -------------------------------------------------------------------------------- 1 | #define ENABLE_NEW_DELETE_TRACE_DUMP 2 | #include "newtrace.hpp" 3 | 4 | template void good() 5 | { 6 | delete (new T); 7 | delete [] (new T [N]); 8 | 9 | struct Boom { Boom() { throw 1; } }; 10 | 11 | try { new Boom; } catch(...) { } // not a leak! 12 | try { new Boom [N]; } catch(...) { } // same! 13 | } 14 | 15 | template void mismatch() 16 | { 17 | delete [] (new T); 18 | delete (new T [N]); 19 | } 20 | 21 | template void leak() 22 | { 23 | /*delete*/ new T; 24 | /*delete []*/ new T [N]; 25 | } 26 | 27 | int main() 28 | { 29 | good(); 30 | mismatch(); 31 | leak(); 32 | } 33 | -------------------------------------------------------------------------------- /src/templates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class NTB 7 | { 8 | public: 9 | virtual ~NTB() {} 10 | virtual void doWork() = 0; 11 | }; 12 | 13 | template 14 | class TC : public NTB 15 | { 16 | public: 17 | TC(T t) : m_T(t) {} 18 | virtual void doWork() override { cout << m_T << endl; } 19 | private: 20 | T m_T; 21 | }; 22 | 23 | int main(int argc, char** argv) 24 | { 25 | vector v; 26 | v.push_back(new TC('X')); 27 | v.push_back(new TC(1)); 28 | v.push_back(new TC(3.141f)); 29 | 30 | for(auto it : v) 31 | it->doWork(); 32 | 33 | for(auto it : v) 34 | delete it; 35 | 36 | return 1; 37 | } 38 | -------------------------------------------------------------------------------- /src/enum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | enum class MyEnum : int 5 | { 6 | V1, 7 | V2, 8 | V3, 9 | SIZE 10 | }; 11 | 12 | const char* MyEnumToString(MyEnum e) 13 | { 14 | using MapType = std::array; 15 | static const MapType kMap = 16 | { 17 | "V1", 18 | "V2", 19 | "V3" 20 | }; 21 | static_assert(kMap.size() == (size_t)MyEnum::SIZE); 22 | return kMap.at((MapType::size_type)e); 23 | } 24 | 25 | int main(int argc, char** argv) 26 | { 27 | std::cout << MyEnumToString(MyEnum::V1) << std::endl; 28 | std::cout << MyEnumToString(MyEnum::V2) << std::endl; 29 | std::cout << MyEnumToString(MyEnum::V3) << std::endl; 30 | return 1; 31 | } 32 | -------------------------------------------------------------------------------- /src/interview_4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | int main(int argc, char** argv) 9 | { 10 | using type = uint8_t; 11 | 12 | for(type number = 0; number < numeric_limits::max(); ++number) 13 | { 14 | bool print = true; 15 | 16 | for(type shift = 0; shift <= numeric_limits::digits - 2; ++shift) 17 | { 18 | static type mask = 0b11; 19 | if((number & (mask << shift)) == (mask << shift)) 20 | { 21 | print = false; 22 | break; 23 | } 24 | } 25 | 26 | if(print) 27 | cout << bitset::digits>(number) << endl; 28 | } 29 | 30 | return 1; 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2021 by Martin Vorbrodt 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 11 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /src/lrpc_proto.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using cmd_t = std::uint8_t; 6 | using seq_t = std::uint16_t; 7 | 8 | enum class CMD : cmd_t 9 | { 10 | Add = 0xAA, 11 | AddRep = 0xBB, 12 | Sub = 0xCC, 13 | SubRep = 0xDD 14 | }; 15 | 16 | #pragma pack(push, 1) 17 | 18 | struct PHead 19 | { 20 | cmd_t cmd; 21 | seq_t seq; 22 | }; 23 | 24 | struct PAdd 25 | { 26 | PHead hdr; 27 | int num1; 28 | int num2; 29 | }; 30 | 31 | struct PAddRep 32 | { 33 | PHead hdr; 34 | int res; 35 | }; 36 | 37 | struct PSub 38 | { 39 | PHead hdr; 40 | int num1; 41 | int num2; 42 | }; 43 | 44 | struct PSubRep 45 | { 46 | PHead hdr; 47 | int res; 48 | }; 49 | 50 | #pragma pack(pop) 51 | -------------------------------------------------------------------------------- /src/interview_1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "event.hpp" 5 | 6 | using namespace std; 7 | 8 | mutex cout_lock; 9 | #define trace(x) { scoped_lock lock(cout_lock); cout << x << endl; } 10 | 11 | const int COUNT = 3; 12 | 13 | int main(int argc, char** argv) 14 | { 15 | auto_event e1, e2; 16 | 17 | thread t1([&](){ 18 | for(int i = 0; i < COUNT; ++i) 19 | { 20 | e1.wait(); 21 | trace("A"); 22 | e2.signal(); 23 | } 24 | }); 25 | 26 | thread t2([&](){ 27 | for(int i = 0; i < COUNT; ++i) 28 | { 29 | e2.wait(); 30 | trace("B"); 31 | e1.signal(); 32 | } 33 | }); 34 | 35 | e1.signal(); 36 | 37 | t1.join(); 38 | t2.join(); 39 | 40 | return 1; 41 | } 42 | -------------------------------------------------------------------------------- /src/attributes.cpp: -------------------------------------------------------------------------------- 1 | [[nodiscard]] int no_discard(int i) 2 | { 3 | switch(i) 4 | { 5 | case 1: 6 | [[fallthrough]]; 7 | [[likely]] case 2: return i; 8 | default: return i; 9 | } 10 | } 11 | 12 | struct E {}; 13 | 14 | struct [[nodiscard]] S 15 | { 16 | int i; 17 | [[no_unique_address]] E e; 18 | }; 19 | 20 | S no_discard_struct() 21 | { 22 | return {}; 23 | } 24 | 25 | struct [[deprecated("Use Q instead")]] P {}; 26 | 27 | [[deprecated("Use bar() instead")]] void foo() {} 28 | 29 | [[noreturn]] void never_returns() 30 | { 31 | while(true); 32 | } 33 | 34 | int main() 35 | { 36 | [[maybe_unused]] int unused = 1; 37 | [[maybe_unused]] P p; 38 | 39 | no_discard(0); 40 | no_discard_struct(); 41 | foo(); 42 | never_returns(); 43 | } 44 | -------------------------------------------------------------------------------- /src/exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main(int, char**) 10 | { 11 | exception_ptr ep = nullptr; 12 | 13 | thread t([&]() 14 | { 15 | try 16 | { 17 | stringstream str; 18 | str << this_thread::get_id(); 19 | throw runtime_error(str.str().c_str()); 20 | } 21 | catch(...) 22 | { 23 | ep = current_exception(); 24 | } 25 | }); 26 | t.join(); 27 | 28 | if(ep != nullptr) 29 | { 30 | try 31 | { 32 | rethrow_exception(ep); 33 | } 34 | catch(exception& e) 35 | { 36 | cout << "Thread " << this_thread::get_id() << " caught exception from thread " << e.what() << endl; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /run_msvc.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set VCPKG_TARGET_TRIPLET=x64-windows 4 | vcpkg install tbb parallelstl boost botan range-v3 benchmark catch2 hayai nonius celero fmt cryptopp wolfssl libmysql thrift protobuf grpc lz4 openssl curl curlpp 5 | 6 | echo. & echo. & echo. 7 | pause 8 | 9 | copy /Y CMakeLists.txt.msvc CMakeLists.txt 10 | copy /Y src\CMakeLists.txt.msvc src\CMakeLists.txt 11 | 12 | del /F /S /Q msvc 13 | del /F /Q msvc 14 | 15 | mkdir msvc 16 | cd msvc 17 | 18 | cmake -DCMAKE_TOOLCHAIN_FILE=C:/Code/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows .. 19 | 20 | cd .. 21 | echo. & echo. & echo. 22 | pause 23 | 24 | msbuild -m msvc/blog.sln -t:Rebuild -p:Configuration=Debug 25 | msbuild -m msvc/blog.sln -t:Rebuild -p:Configuration=Release 26 | 27 | echo. & echo. & echo. 28 | pause 29 | -------------------------------------------------------------------------------- /src/dekker_n.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | const int COUNT = 5; 9 | const int THREADS = thread::hardware_concurrency(); 10 | const int THREAD_MASK = 0b1; 11 | 12 | int main(int argc, char** argv) 13 | { 14 | atomic_uint flag{0}; 15 | 16 | auto proc = [&](int t, unsigned int thread_mask) { 17 | for(int i = 0; i < COUNT;) 18 | { 19 | if(flag.fetch_or(thread_mask) == 0) 20 | { 21 | cout << "T" << t << " in critical section" << endl; 22 | ++i; 23 | } 24 | 25 | flag.fetch_xor(thread_mask); 26 | } 27 | }; 28 | 29 | vector vt; 30 | for(int i = 0; i < THREADS; ++i) 31 | vt.emplace_back(proc, i, THREAD_MASK << i); 32 | 33 | for(auto& t : vt) 34 | t.join(); 35 | 36 | return 1; 37 | } 38 | -------------------------------------------------------------------------------- /src/queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "queue.hpp" 5 | 6 | using namespace std; 7 | 8 | const int COUNT = 10; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | bounded_queue q(5); 13 | mutex cout_lock; 14 | 15 | thread producer([&]() { 16 | for(int i = 1; i <= COUNT; ++i) 17 | { 18 | q.push(i); 19 | { 20 | scoped_lock lock(cout_lock); 21 | cout << "push v = " << i << endl; 22 | } 23 | } 24 | }); 25 | 26 | thread consumer([&]() { 27 | for(int i = 1; i <= COUNT; ++i) 28 | { 29 | this_thread::sleep_for(1s); 30 | int v; 31 | q.pop(v); 32 | { 33 | scoped_lock lock(cout_lock); 34 | cout << "pop v = " << v << endl; 35 | } 36 | } 37 | }); 38 | 39 | producer.join(); 40 | consumer.join(); 41 | 42 | return 1; 43 | } 44 | -------------------------------------------------------------------------------- /src/xmlrpc_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | class hello : public xmlrpc_c::method 11 | { 12 | public: 13 | void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval) 14 | { 15 | string msg(params.getString(0)); 16 | params.verifyEnd(1); 17 | 18 | cout << msg << endl; 19 | 20 | *retval = xmlrpc_c::value_string("XMLRPC server says hello!"); 21 | } 22 | }; 23 | 24 | int main(int argc, char** argv) 25 | { 26 | xmlrpc_c::registry registry; 27 | registry.addMethod("hello", new hello); 28 | 29 | xmlrpc_c::serverAbyss server(xmlrpc_c::serverAbyss::constrOpt().registryP(®istry).portNumber(8080)); 30 | server.run(); 31 | 32 | return 1; 33 | } 34 | -------------------------------------------------------------------------------- /src/round.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "round.hpp" 5 | 6 | //#define my_round(V, D) runtime_round1(V, D) 7 | //#define my_round(V, D) runtime_round2(V, D) 8 | #define my_round(V, D) constexpr_round(V) 9 | 10 | int main() 11 | { 12 | using namespace std; 13 | 14 | constexpr auto v = 0.555555555555555; 15 | 16 | using T = decltype(v); 17 | 18 | auto a = array 19 | { 20 | my_round(v, 1), my_round(v, 2), my_round(v, 3), my_round(v, 4), my_round(v, 5), 21 | my_round(v, 6), my_round(v, 7), my_round(v, 8), my_round(v, 9), my_round(v, 10), 22 | my_round(v, 11), my_round(v, 12), my_round(v, 13), my_round(v, 14), my_round(v, 15), 23 | }; 24 | 25 | for(auto p = 1; auto v : a) 26 | { 27 | cout << fixed << setprecision(p + 1); 28 | cout << p++ << ":\t" << v << endl; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Interface1 5 | { 6 | public: 7 | virtual void Method1() = 0; 8 | }; 9 | 10 | class Object1 : public Interface1 11 | { 12 | public: 13 | virtual void Method1() override {} 14 | }; 15 | 16 | void Codebase1(Interface1* obj) {} 17 | 18 | class Interface2 19 | { 20 | public: 21 | virtual void Method2() = 0; 22 | }; 23 | 24 | class Object2 : public Interface2 25 | { 26 | public: 27 | virtual void Method2() override {} 28 | }; 29 | 30 | void Codebase2(Interface2* obj) {} 31 | 32 | class Adapter : public Interface1 33 | { 34 | public: 35 | explicit Adapter(Interface2* o) : obj{ o } {} 36 | virtual void Method1() override { obj->Method2(); } 37 | private: 38 | Interface2* obj = nullptr; 39 | }; 40 | 41 | int main() 42 | { 43 | Object2 obj; 44 | Adapter adp(&obj); 45 | Codebase1(&adp); 46 | } 47 | -------------------------------------------------------------------------------- /src/dekker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const int COUNT = 10; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | atomic_bool f1{false}, f2{false}; 12 | 13 | auto proc1 = [&]() { 14 | for(int i = 0; i < COUNT;) 15 | { 16 | f1.store(true); 17 | if(f2.load() == false) 18 | { 19 | cout << "T1 in critical section" << endl; 20 | ++i; 21 | } 22 | 23 | f1.store(false); 24 | } 25 | }; 26 | 27 | auto proc2 = [&]() { 28 | for(int i = 0; i < COUNT;) 29 | { 30 | f2.store(true); 31 | if(f1.load() == false) 32 | { 33 | cout << "T2 in critical section" << endl; 34 | ++i; 35 | } 36 | 37 | f2.store(false); 38 | } 39 | }; 40 | 41 | thread t1(proc1); 42 | thread t2(proc2); 43 | 44 | t1.join(); 45 | t2.join(); 46 | 47 | return 1; 48 | } 49 | -------------------------------------------------------------------------------- /src/plugin_load.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | using namespace boost::dll; 7 | using namespace boost::filesystem; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | if (argc != 2) 12 | { 13 | cerr << "USAGE: " << argv[0] << " [path to plugin.dll]" << endl; 14 | return 1; 15 | } 16 | 17 | path lib_path(argv[1]); 18 | 19 | typedef const char* (plugin_name_t)(); 20 | typedef const char* (plugin_version_t)(); 21 | 22 | auto plugin_name = import_alias(lib_path, "plugin_name", load_mode::append_decorations); 23 | auto plugin_version = import_alias(lib_path, "plugin_version", load_mode::append_decorations); 24 | 25 | cout << "Plugin name : " << plugin_name() << endl; 26 | cout << "Plugin version : " << plugin_version() << endl; 27 | } 28 | -------------------------------------------------------------------------------- /src/constructors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct M 7 | { 8 | M(const char* s) : m_s(s) { cout << "M::M(m_s = '" << m_s << "')" << endl; } 9 | ~M() { cout << "M::~M(m_s = '" << m_s << "')" << endl; } 10 | 11 | string m_s; 12 | }; 13 | 14 | struct S 15 | { 16 | S() : S(2019) { cout << "S::S() throws..." << endl; throw runtime_error("BOOM!"); } 17 | S(int i) : S("C++") { cout << "S::S(int i = '" << i << "')" << endl; } 18 | S(const char* s) : m_vM("by value"), m_pM(make_unique("by pointer")) 19 | { cout << "S::S(const char* s = '" << s << "')" << endl; } 20 | ~S() { cout << "S::~S() cleans up..." << endl; } 21 | 22 | M m_vM; 23 | unique_ptr m_pM = nullptr; 24 | }; 25 | 26 | int main() 27 | { 28 | try 29 | { 30 | S s; 31 | } 32 | catch(exception& e) 33 | { 34 | cout << e.what() << endl; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/inverting.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | mutex cout_lock; 10 | #define trace(x) { scoped_lock lock(cout_lock); cout << x << endl; } 11 | 12 | const int COUNT = 5; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | srand((unsigned int)time(NULL)); 17 | 18 | multimap m1; 19 | for(int i = 0; i < COUNT; ++i) 20 | m1.insert(make_pair(rand() % 10, rand() % 10)); 21 | 22 | trace("Source (Key -> Value):"); 23 | for(const auto& it : m1) 24 | trace(it.first << " -> " << it.second); 25 | 26 | multimap m2; 27 | for(const auto& it : m1) 28 | m2.insert(make_pair(it.second, it.first)); 29 | 30 | trace("Output (Value -> Key):"); 31 | for(const auto& it : m2) 32 | trace(it.first << " -> " << it.second); 33 | 34 | return 1; 35 | } 36 | -------------------------------------------------------------------------------- /src/assignment.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct S 7 | { 8 | S() 9 | : m_resource(new int) 10 | { 11 | cout << "S()" << endl; 12 | *m_resource = 1; 13 | } 14 | 15 | S(const S& s) 16 | : m_resource(new int) 17 | { 18 | cout << "S(const S&)" << endl; 19 | *m_resource = *s.m_resource; 20 | } 21 | 22 | S(S&&) = delete; 23 | 24 | S& operator = (const S& s) 25 | { 26 | cout << "operator = (const S&)" << endl; 27 | S temp(s); // May throw 28 | swap(temp); // Will never throw 29 | return *this; 30 | } 31 | 32 | S& operator = (S&&) = delete; 33 | 34 | ~S() 35 | { 36 | cout << "~S()" << endl; 37 | delete m_resource; 38 | } 39 | 40 | void swap(S& s) noexcept 41 | { 42 | std::swap(m_resource, s.m_resource); 43 | } 44 | 45 | int* m_resource; 46 | }; 47 | 48 | int main() 49 | { 50 | S s1, s2; 51 | s1 = s2; 52 | } 53 | -------------------------------------------------------------------------------- /src/cache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace chrono; 8 | 9 | const int CACHE_LINE_SIZE = 64;//sizeof(int); 10 | const int SIZE = CACHE_LINE_SIZE / sizeof(int) + 1; 11 | const int COUNT = 100'000'000; 12 | 13 | int main(int argc, char** argv) 14 | { 15 | srand((unsigned int)time(NULL)); 16 | 17 | int* p = new int [SIZE]; 18 | 19 | auto proc = [](int* data) { 20 | for(int i = 0; i < COUNT; ++i) 21 | *data = *data + rand(); 22 | }; 23 | 24 | auto start_time = high_resolution_clock::now(); 25 | 26 | std::thread t1(proc, &p[0]); 27 | std::thread t2(proc, &p[SIZE - 1]); 28 | 29 | t1.join(); 30 | t2.join(); 31 | 32 | auto end_time = high_resolution_clock::now(); 33 | cout << "Duration: " << duration_cast(end_time - start_time).count() / 1000.f << " ms" << endl; 34 | 35 | return 1; 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.filters 2 | *.sln 3 | *.user 4 | *.vcxproj 5 | *.xcodeproj/ 6 | .DS_Store 7 | .idea/ 8 | .vs/ 9 | .vscode/ 10 | CMakeCache.txt 11 | CMakeFiles/ 12 | CMakeLists.txt 13 | CMakeScripts/ 14 | Makefile 15 | bin/ 16 | blog.build/ 17 | blog.code-workspace 18 | build/ 19 | cmake-build-debug/ 20 | cmake-build-release/ 21 | cmake-build-minsizerel/ 22 | cmake-build-relwithdebinfo/ 23 | cmake_install.cmake 24 | llvm/ 25 | gcc/ 26 | msvc/ 27 | src/*.dir 28 | src/*.filters 29 | src/*.vcxproj 30 | src/CMakeFiles/ 31 | src/CMakeLists.txt 32 | src/CMakeScripts/ 33 | src/Makefile 34 | src/blog.build/ 35 | src/cmake_install.cmake 36 | src/ThriftService.cpp 37 | src/ThriftService.h 38 | src/ThriftService_server.skeleton.cpp 39 | src/grpc_service.grpc.pb.cc 40 | src/grpc_service.grpc.pb.h 41 | src/grpc_service.pb.cc 42 | src/grpc_service.pb.h 43 | src/protobuf.pb.cc 44 | src/protobuf.pb.h 45 | src/thrift_service_types.h 46 | vs/ 47 | x64/ 48 | xcode/ 49 | -------------------------------------------------------------------------------- /src/random_test.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | const int COUNT = 10'000'000; 9 | 10 | TEST_CASE("random", "[benchmark]") 11 | { 12 | random_device rd; 13 | mt19937 mt(rd()); 14 | uniform_int_distribution int_dist(-10, 10); 15 | uniform_real_distribution real_dist(1.0, 10.0); 16 | 17 | int result{}; 18 | double dresult{}; 19 | 20 | BENCHMARK("random_device") 21 | { 22 | for (int i = 0; i < COUNT; ++i) 23 | result += rd(); 24 | }; 25 | 26 | BENCHMARK("mt19937") 27 | { 28 | for (int i = 0; i < COUNT; ++i) 29 | result += mt(); 30 | }; 31 | 32 | BENCHMARK("uniform_int_distribution") 33 | { 34 | for (int i = 0; i < COUNT; ++i) 35 | result += int_dist(mt); 36 | }; 37 | 38 | BENCHMARK("uniform_real_distribution") 39 | { 40 | for (int i = 0; i < COUNT; ++i) 41 | dresult += real_dist(mt); 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/ranges.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | namespace r = ::ranges; 8 | namespace rv = ::ranges::views; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | using Ints = vector; 13 | Ints numbers = { 1, 2, 3, 4, 5 }; 14 | 15 | auto evenNumbers = numbers 16 | | rv::filter([](int n){ return n % 2 == 0; }) 17 | | rv::transform([](int n) { return n * 2; }); 18 | 19 | for(auto _ : evenNumbers) 20 | cout << _ << " "; 21 | cout << endl; 22 | 23 | auto xs = rv::ints(1, 11) // Make int range, 1 to 10... 11 because stl ranges exclude last element 24 | | rv::transform([](auto i) { return std::make_pair(i, i * i); }) // transform each int in range 25 | | r::to(); // convert generated range to standard vector 26 | 27 | for_each(begin(xs), end(xs), [](auto& p) { cout << p.first << ", " << p.second << endl; }); 28 | 29 | return 1; 30 | } 31 | -------------------------------------------------------------------------------- /src/interview_5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | void print_runs(const char* input) 6 | { 7 | size_t offset = 0; 8 | size_t index = 0; 9 | 10 | while(char current = input[index]) 11 | { 12 | char next = input[index + 1]; 13 | if(next && next == current) 14 | { 15 | cout << " " << offset; 16 | while(input[index] && current == input[index]) ++index; 17 | offset = index; 18 | } 19 | else 20 | { 21 | offset = ++index; 22 | } 23 | } 24 | } 25 | 26 | int main(int argv, char** argc) 27 | { 28 | const char* inputs[] = 29 | { 30 | "0000000000", 31 | "0101010101", 32 | "1111111111", 33 | "0110110110", 34 | "1110000011", 35 | "0010011010" 36 | }; 37 | 38 | for(size_t index = 0; index < (sizeof(inputs) / sizeof(*inputs)); ++index) 39 | { 40 | cout << "Input: " << inputs[index] << endl; 41 | cout << "Runs :"; 42 | print_runs(inputs[index]); 43 | cout << endl << endl; 44 | } 45 | 46 | return 1; 47 | } 48 | -------------------------------------------------------------------------------- /src/bloom.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bloom.hpp" 5 | 6 | using namespace std; 7 | 8 | int main() 9 | { 10 | auto set1 = {"Martin", "Vorbrodt", "C++", "Blog"}; 11 | auto set2 = {"Not", "In", "The", "Set"}; 12 | 13 | bloom_filter bloom(128/*, 3*/); 14 | for(auto s : set1) bloom.add(s); 15 | 16 | std::boolalpha(cout); 17 | cout << "bloom_filter:" << endl; 18 | for(auto s : set1) cout << "\tContains \"" << s << "\"\t: " << bloom.contains(s) << endl; 19 | for(auto s : set2) cout << "\tContains \"" << s << "\"\t: " << bloom.contains(s) << endl; 20 | cout << endl; 21 | 22 | fixed_bloom_filter fixed_bloom; 23 | for(auto s : set1) fixed_bloom.add(s); 24 | 25 | cout << "fixed_bloom_filter:" << endl; 26 | for(auto s : set1) cout << "\tContains \"" << s << "\"\t: " << fixed_bloom.contains(s) << endl; 27 | for(auto s : set2) cout << "\tContains \"" << s << "\"\t: " << fixed_bloom.contains(s) << endl; 28 | } 29 | -------------------------------------------------------------------------------- /src/thrift_service_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ThriftService.h" 6 | 7 | using namespace std; 8 | using namespace apache::thrift; 9 | using namespace apache::thrift::protocol; 10 | using namespace apache::thrift::transport; 11 | using namespace ThriftService; 12 | 13 | int main(int argc, char** argv) 14 | { 15 | auto socket = make_shared("localhost", 9090); 16 | auto transport = make_shared(socket); 17 | auto protocol = make_shared(transport); 18 | 19 | ThriftServiceClient client(protocol); 20 | 21 | try 22 | { 23 | transport->open(); 24 | 25 | client.ping(); 26 | client.hello("Martin says hi!"); 27 | client.async_call(); 28 | 29 | transport->close(); 30 | } 31 | catch(TException& tx) 32 | { 33 | cout << "ERROR: " << tx.what() << endl; 34 | } 35 | 36 | return 1; 37 | } 38 | -------------------------------------------------------------------------------- /src/grpc_service_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "grpc_service.grpc.pb.h" 8 | 9 | using namespace std; 10 | using namespace grpc; 11 | 12 | class gRPCServiceImpl final : public gRPCService::Service 13 | { 14 | public: 15 | Status hello(ServerContext* context, const Input* in, Output* out) override 16 | { 17 | cout << in->input_msg() << endl; 18 | out->set_output_msg("gRPC server says hi!"); 19 | return Status::OK; 20 | } 21 | }; 22 | 23 | int main(int argc, char** argv) 24 | { 25 | gRPCServiceImpl service; 26 | ServerBuilder builder; 27 | builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials()); 28 | builder.RegisterService(&service); 29 | unique_ptr server(builder.BuildAndStart()); 30 | cout << "Server starting..." << endl; 31 | server->Wait(); 32 | 33 | return 1; 34 | } 35 | -------------------------------------------------------------------------------- /src/pstl_test_gcc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | using namespace std; 12 | using namespace std::chrono; 13 | using namespace std::execution; 14 | 15 | auto gen = mt19937_64{ random_device()() }; 16 | auto data = vector(250'000'000); 17 | 18 | auto start = steady_clock::now(); 19 | 20 | generate(begin(data), end(data), gen); 21 | sort(begin(data), end(data)); 22 | is_sorted(begin(data), end(data)); 23 | 24 | auto stop = steady_clock::now(); 25 | cout << "STL took " << duration_cast(stop - start) << endl; 26 | 27 | start = steady_clock::now(); 28 | 29 | generate(par_unseq, begin(data), end(data), gen); 30 | sort(par_unseq, begin(data), end(data)); 31 | is_sorted(par_unseq, begin(data), end(data)); 32 | 33 | stop = steady_clock::now(); 34 | cout << "PSTL took " << duration_cast(stop - start) << endl; 35 | } 36 | -------------------------------------------------------------------------------- /src/atomics.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | const int COUNT = 3; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | atomic e1{false}, e2{false}, e3{false}; 12 | 13 | thread t1([&](){ 14 | for(int i = 0; i < COUNT; ++i) 15 | { 16 | bool e = true; 17 | while(!e1.compare_exchange_strong(e, false)) e = true; 18 | cout << "A" << endl; 19 | e2.store(true); 20 | } 21 | }); 22 | 23 | thread t2([&](){ 24 | for(int i = 0; i < COUNT; ++i) 25 | { 26 | bool e = true; 27 | while(!e2.compare_exchange_strong(e, false)) e = true; 28 | cout << "B" << endl; 29 | e3.store(true); 30 | } 31 | }); 32 | 33 | thread t3([&](){ 34 | for(int i = 0; i < COUNT; ++i) 35 | { 36 | bool e = true; 37 | while(!e3.compare_exchange_strong(e, false)) e = true; 38 | cout << "C" << endl; 39 | e1.store(true); 40 | } 41 | }); 42 | 43 | e1.store(true); 44 | 45 | t1.join(); 46 | t2.join(); 47 | t3.join(); 48 | 49 | return 1; 50 | } 51 | -------------------------------------------------------------------------------- /src/cpu_timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace boost::timer; 9 | 10 | int main() 11 | { 12 | srand((unsigned int)time(NULL)); 13 | 14 | auto_cpu_timer program_timer(3); 15 | 16 | auto procUser = [](long work) 17 | { 18 | for (long i = 0; i < work; ++i) 19 | sqrt(123.456L); 20 | }; 21 | 22 | auto procSystem = [](long work) 23 | { 24 | for (long i = 0; i < work; ++i) 25 | thread([](){}).detach(); 26 | }; 27 | 28 | auto procTimer = [](long work) 29 | { 30 | cpu_timer timer; 31 | timer.start(); 32 | 33 | for(long i = 0; i < work; ++i) 34 | rand(); 35 | 36 | timer.stop(); 37 | cout << "Thread timer:" << timer.format(3); 38 | }; 39 | 40 | thread t1(procUser, 1000000000); 41 | thread t2(procSystem, 100000); 42 | thread t3(procTimer, 100000000); 43 | 44 | t1.join(); 45 | t2.join(); 46 | t3.join(); 47 | 48 | cout << "Program timer:"; 49 | 50 | return 1; 51 | } 52 | -------------------------------------------------------------------------------- /cmake/FindCURLpp.cmake: -------------------------------------------------------------------------------- 1 | #~ finds curlpp 2 | 3 | find_package(CURL REQUIRED) 4 | 5 | set(store_cfls ${CMAKE_FIND_LIBRARY_SUFFIXES}) 6 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so") 7 | set(CURLPP_FIND_NAMES curlpp libcurlpp) 8 | set(CURLPP_INCLUDE_PREFIX "curlpp/") 9 | #~ set(CURLPP_INCLUDE_SEARCHES "Easy.hpp" "cURLpp.hpp" "Info.hpp" "Infos.hpp" "Option.hpp" "Options.hpp" "Form.hpp") 10 | set(CURLPP_INCLUDE_SEARCHES "cURLpp.hpp") 11 | 12 | 13 | find_path(CURLPP_INCLUDE_DIR NAMES ${CURLPP_INCLUDE_SEARCHES} PATH_SUFFIXES ${CURLPP_INCLUDE_PREFIX}) 14 | find_library(CURLPP_LIBRARY NAMES ${CURLPP_FIND_NAMES} PATHS "/usr/local/lib") 15 | 16 | set(CURLPP_LIBRARIES ${CURL_LIBRARIES} ${CURLPP_LIBRARY}) 17 | set(CURLPP_INCLUDE_DIRS ${CURL_INCLUDE_DIRS} ${CURLPP_INCLUDE_DIR}) 18 | 19 | include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) 20 | find_package_handle_standard_args(CURLpp DEFAULT_MSG CURLPP_LIBRARY CURLPP_INCLUDE_DIR) 21 | 22 | mark_as_advanced(CURLPP_LIBRARY CURLPP_INCLUDE_DIR) 23 | set(CMAKE_FIND_LIBRARY_SUFFIXES "${store_cfls}") 24 | -------------------------------------------------------------------------------- /src/pstl_test.cpp: -------------------------------------------------------------------------------- 1 | // #define CATCH_CONFIG_MAIN 2 | // #define CATCH_CONFIG_ENABLE_BENCHMARKING 3 | #include 4 | #if defined(_LIBCPP_HAS_PARALLEL_ALGORITHMS) or defined(_WIN64) 5 | #include 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | 15 | TEST_CASE("STL vs PSTL", "[benchmark]") 16 | { 17 | using namespace std; 18 | using namespace std::execution; 19 | 20 | auto gen = mt19937_64{ random_device()() }; 21 | auto data = vector(1'000'000); 22 | 23 | BENCHMARK("STL") 24 | { 25 | generate(begin(data), end(data), gen); 26 | sort(begin(data), end(data)); 27 | [[maybe_unused]] auto is = is_sorted(begin(data), end(data)); 28 | }; 29 | 30 | BENCHMARK("PSTL") 31 | { 32 | generate(par_unseq, begin(data), end(data), gen); 33 | sort(par_unseq, begin(data), end(data)); 34 | [[maybe_unused]] auto is = is_sorted(par_unseq, begin(data), end(data)); 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/try_block.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct P 7 | { 8 | P() { throw logic_error("Logic error from P::P()"); } 9 | }; 10 | 11 | struct Q 12 | { 13 | Q() try : m_P() 14 | { 15 | cout << "Never printed!" << endl; 16 | } 17 | catch(exception& e) 18 | { 19 | cout << "Inside Q::Q() caught: " << e.what() << endl; 20 | throw runtime_error("Runtime error from Q::Q()"); 21 | } 22 | 23 | P m_P; 24 | }; 25 | 26 | void eat_it() 27 | { 28 | try 29 | { 30 | throw runtime_error("System error from eat_it()"); 31 | } 32 | catch(exception& e) 33 | { 34 | cout << "Swallowing: " << e.what() << endl; 35 | } 36 | } 37 | 38 | void eat_it_sugar() try 39 | { 40 | throw runtime_error("System error from eat_it_sugar()"); 41 | } 42 | catch(exception& e) 43 | { 44 | cout << "Swallowing: " << e.what() << endl; 45 | } 46 | 47 | int main() try 48 | { 49 | eat_it(); 50 | eat_it_sugar(); 51 | Q q; 52 | } 53 | catch(exception& e) 54 | { 55 | cout << "Inside main() caught: " << e.what() << endl; 56 | } 57 | -------------------------------------------------------------------------------- /src/newtrace.st.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "newtrace.st.hpp" 6 | 7 | const int N = 100; 8 | 9 | const bool D = false; // true = GOOD CODE, false = BAD BAD CODE 10 | 11 | int main() 12 | { 13 | char* cp = new char; 14 | char* ca = new char [N]; 15 | short* sp = new short; 16 | short* sa = new short [N]; 17 | int* ip = new int; 18 | int* ia = new int [N]; 19 | 20 | if(D) 21 | { 22 | delete cp; 23 | delete [] ca; 24 | delete sp; 25 | delete [] sa; 26 | delete ip; 27 | delete [] ia; 28 | } 29 | 30 | if(auto leaks = get_leaks(); !leaks.empty()) 31 | { 32 | std::cerr << "\n"; 33 | std::cerr << "**************************\n"; 34 | std::cerr << "*** MEMORY LEAK REPORT ***\n"; 35 | std::cerr << "**************************\n\n"; 36 | 37 | for(const auto& entry : leaks) 38 | { 39 | std::cerr << "! " << entry.bytes << " bytes -> (" << entry.file << " : " << entry.proc << ", line " << entry.line << ")\n"; 40 | std::cerr << entry.stack << "\n"; 41 | } 42 | std::cerr << "\n"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/interview_2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | mutex cout_lock; 8 | #define trace(x) { scoped_lock lock(cout_lock); cout << x << endl; } 9 | 10 | template 11 | class queue 12 | { 13 | public: 14 | void push(const T& item) 15 | { 16 | spill(m_pop, m_push); 17 | m_push.push(item); 18 | } 19 | 20 | void pop(T& item) 21 | { 22 | spill(m_push, m_pop); 23 | item = m_pop.top(); 24 | m_pop.pop(); 25 | } 26 | 27 | bool empty() const noexcept 28 | { 29 | return m_push.empty() && m_pop.empty(); 30 | } 31 | 32 | private: 33 | void spill(stack& from, stack& to) 34 | { 35 | while(!from.empty()) 36 | { 37 | to.push(from.top()); 38 | from.pop(); 39 | } 40 | } 41 | 42 | stack m_push; 43 | stack m_pop; 44 | }; 45 | 46 | int main(int argc, char** argv) 47 | { 48 | ::queue q; 49 | 50 | q.push(1); 51 | q.push(2); 52 | q.push(3); 53 | 54 | while(!q.empty()) 55 | { 56 | int item; 57 | q.pop(item); 58 | trace(item); 59 | } 60 | 61 | return 1; 62 | } 63 | -------------------------------------------------------------------------------- /src/deep_ptr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "deep_ptr.hpp" 4 | 5 | using namespace std; 6 | 7 | struct S 8 | { 9 | S() = default; 10 | S(const S&) { cout << "using S(const S&)" << endl; } 11 | virtual ~S() = default; 12 | 13 | virtual S* clone() const { 14 | cout << "using S::clone()" << endl; 15 | return new S; } 16 | }; 17 | 18 | int main() 19 | { 20 | auto p1 = make_deep(); 21 | auto p2 = p1; 22 | auto p3 = std::move(p1); 23 | p1 = std::move(p3); 24 | p3 = p2; 25 | p2 = nullptr; 26 | 27 | auto clone = [](S* p) { return p->clone(); }; 28 | using clone_ptr = deep_ptr; 29 | 30 | auto p4 = clone_ptr(new S, clone); 31 | auto p5 = p4; 32 | auto p6 = std::move(p4); 33 | p4 = std::move(p6); 34 | p6 = p5; 35 | p5 = nullptr; 36 | 37 | auto del = [](S* p) { 38 | cout << "using 'del' deleter" << endl; 39 | delete p; }; 40 | using del_ptr = deep_ptr; 41 | 42 | auto p7 = del_ptr(new S, clone, del); 43 | auto p8 = p7; 44 | auto p9 = std::move(p7); 45 | p7 = std::move(p9); 46 | p9 = p8; 47 | p8 = nullptr; 48 | } 49 | -------------------------------------------------------------------------------- /src/memory_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "memory_pool.hpp" 6 | 7 | int main() 8 | { 9 | using namespace std; 10 | 11 | constexpr auto I = 3; 12 | constexpr auto BLOCKS = 3; 13 | constexpr auto CHUNK_SIZE = 10; 14 | constexpr auto CHUNKS_PER_BLOCK = 10; 15 | constexpr auto CHUNKS_TO_MALLOC = CHUNKS_PER_BLOCK * BLOCKS; 16 | constexpr auto BLOCK_RESERVE = 1; 17 | 18 | using chunks_t = std::vector; 19 | 20 | auto chunks = chunks_t{}; 21 | auto gen = std::mt19937{ (std::random_device{})() }; 22 | 23 | auto pool = memory_pool{}; 24 | pool.reserve_blocks(BLOCK_RESERVE); 25 | 26 | cout << pool << endl << endl; 27 | 28 | for(auto i = 0; i < I; ++i) 29 | { 30 | for(auto c = 0; c < CHUNKS_TO_MALLOC - i; ++c) 31 | chunks.push_back(pool.malloc()); 32 | cout << pool << endl << endl; 33 | 34 | std::shuffle(std::begin(chunks), std::end(chunks), gen); 35 | 36 | for(auto chunk : chunks) 37 | pool.free(chunk); 38 | cout << pool << endl << endl; 39 | 40 | chunks.clear(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/enum2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MY_ENUM \ 4 | X(V1, -1) \ 5 | X(V2, -3) \ 6 | X(V3, -5) 7 | 8 | #define X(name, value) name = value, 9 | 10 | #define MY_ENUM_NAME MyEnum 11 | #define MY_ENUM_TYPE int 12 | 13 | enum class MY_ENUM_NAME : MY_ENUM_TYPE 14 | { 15 | MY_ENUM 16 | }; 17 | 18 | #undef X 19 | 20 | constexpr auto MyEnumToString(MY_ENUM_NAME e) noexcept 21 | { 22 | #define X(name, value) case(MY_ENUM_NAME::name): return #name; 23 | switch(e) 24 | { 25 | MY_ENUM 26 | } 27 | #undef X 28 | return "UNKNOWN"; 29 | } 30 | 31 | int main(int argc, char** argv) 32 | { 33 | std::cout << "value = " << (MY_ENUM_TYPE)MyEnum::V1 << ", name = " << MyEnumToString(MY_ENUM_NAME::V1) << std::endl; 34 | std::cout << "value = " << (MY_ENUM_TYPE)MyEnum::V2 << ", name = " << MyEnumToString(MY_ENUM_NAME::V2) << std::endl; 35 | std::cout << "value = " << (MY_ENUM_TYPE)MyEnum::V3 << ", name = " << MyEnumToString(MY_ENUM_NAME::V3) << std::endl; 36 | 37 | MY_ENUM_NAME unknown{-42}; 38 | std::cout << "value = " << (MY_ENUM_TYPE)unknown << ", name = " << MyEnumToString(unknown) << std::endl; 39 | 40 | return 1; 41 | } 42 | -------------------------------------------------------------------------------- /src/delete.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | void any_type(void* p) { cout << "any_type(void* p = " << hex << p << ")\n"; } 6 | 7 | void void_only(std::nullptr_t) { cout << "void_only(std::nullptr_t)\n"; } 8 | void void_only(void* p) { cout << "void_only(void* p = " << hex << p << ")\n"; } 9 | 10 | #if __cplusplus >= 202002L 11 | void void_only(auto*) = delete; // C++20 and newer... 12 | #else 13 | template void void_only(T*) = delete; // prior to C++20... 14 | #endif 15 | 16 | // ALL other overloads, not just 1 pointer parameter signatures... 17 | template void void_only(Ts&&...) = delete; 18 | 19 | int main() 20 | { 21 | any_type(new char); 22 | any_type(new short); 23 | any_type(new int); 24 | 25 | void_only(nullptr); // 1st overload 26 | void_only((void*)0xABC); // 2nd overload, type must be void* 27 | 28 | // void_only(0); // ERROR, ambiguous 29 | // void_only(NULL); // ERROR, ambiguous 30 | // void_only((long*)0); // ERROR, explicitly deleted 31 | // void_only((int*)1, (int*)2); // ERROR, also explicitly deleted 32 | } 33 | -------------------------------------------------------------------------------- /cmake/Findlz4.cmake: -------------------------------------------------------------------------------- 1 | # Finds liblz4. 2 | # 3 | # This module defines: 4 | # LZ4_FOUND 5 | # LZ4_INCLUDE_DIR 6 | # LZ4_LIBRARY 7 | # 8 | 9 | find_path(LZ4_INCLUDE_DIR NAMES lz4.h) 10 | find_library(LZ4_LIBRARY NAMES lz4) 11 | 12 | # We require LZ4_compress_default() which was added in v1.7.0 13 | if (LZ4_LIBRARY) 14 | include(CheckCSourceRuns) 15 | set(CMAKE_REQUIRED_INCLUDES ${LZ4_INCLUDE_DIR}) 16 | set(CMAKE_REQUIRED_LIBRARIES ${LZ4_LIBRARY}) 17 | check_c_source_runs(" 18 | #include 19 | int main() { 20 | int good = (LZ4_VERSION_MAJOR > 1) || 21 | ((LZ4_VERSION_MAJOR == 1) && (LZ4_VERSION_MINOR >= 7)); 22 | return !good; 23 | }" LZ4_GOOD_VERSION) 24 | set(CMAKE_REQUIRED_INCLUDES) 25 | set(CMAKE_REQUIRED_LIBRARIES) 26 | endif() 27 | 28 | include(FindPackageHandleStandardArgs) 29 | FIND_PACKAGE_HANDLE_STANDARD_ARGS( 30 | LZ4 DEFAULT_MSG 31 | LZ4_LIBRARY LZ4_INCLUDE_DIR LZ4_GOOD_VERSION) 32 | 33 | if (NOT LZ4_FOUND) 34 | message(STATUS "Using third-party bundled LZ4") 35 | else() 36 | message(STATUS "Found LZ4: ${LZ4_LIBRARY}") 37 | endif (NOT LZ4_FOUND) 38 | 39 | mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY) 40 | -------------------------------------------------------------------------------- /src/alignas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int main() 6 | { 7 | struct Old 8 | { 9 | int x; 10 | char padding[16 - sizeof(int)]; 11 | }; 12 | cout << "sizeof(Old): " << sizeof(Old) << endl << endl; 13 | 14 | struct alignas(16) New 15 | { 16 | int x; 17 | }; 18 | cout << "sizeof(New): " << sizeof(New) << endl << endl; 19 | 20 | alignas(16) int x{}, y{}; 21 | alignas(16) int z{}; 22 | ptrdiff_t delta1 = (uint8_t*)&y - (uint8_t*)&x; 23 | ptrdiff_t delta2 = (uint8_t*)&z - (uint8_t*)&y; 24 | cout << "Address of 'x' : " << &x << endl; 25 | cout << "Address of 'y' : " << &y << endl; 26 | cout << "Address of 'z' : " << &z << endl; 27 | cout << "Distance 'x' to 'y' : " << delta1 << endl; 28 | cout << "Distance 'y' to 'z' : " << delta2 << endl << endl; 29 | 30 | struct Empty {}; 31 | struct alignas(64) Empty64 {}; 32 | 33 | cout << "sizeof(Empty) : " << sizeof(Empty) << endl; 34 | cout << "sizeof(Empty64): " << sizeof(Empty64) << endl << endl; 35 | 36 | struct Full 37 | { 38 | alignas(32) char c; 39 | alignas(16) int x, y; 40 | }; 41 | cout << "sizeof(Full): " << sizeof(Full) << endl << endl; 42 | } 43 | -------------------------------------------------------------------------------- /src/thrift_service_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "ThriftService.h" 7 | 8 | using namespace std; 9 | using namespace apache::thrift; 10 | using namespace apache::thrift::protocol; 11 | using namespace apache::thrift::transport; 12 | using namespace apache::thrift::server; 13 | using namespace ThriftService; 14 | 15 | class ThriftServiceHandler : public ThriftServiceIf 16 | { 17 | public: 18 | ThriftServiceHandler() = default; 19 | 20 | void ping() override { cout << "ping()" << endl; } 21 | void hello(const string& msg) override { cout << msg << endl; } 22 | void async_call() override { cout << "async_call()" << endl; } 23 | }; 24 | 25 | int main(int argc, char** argv) 26 | { 27 | TSimpleServer server( 28 | std::make_shared(std::make_shared()), 29 | std::make_shared(9090), 30 | std::make_shared(), 31 | std::make_shared()); 32 | 33 | cout << "Starting the server..." << endl; 34 | 35 | server.serve(); 36 | 37 | return 1; 38 | } 39 | -------------------------------------------------------------------------------- /src/compression.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | using buffer = vector; 8 | 9 | void lz4_compress(const buffer& in, buffer& out) 10 | { 11 | auto rv = LZ4_compress_default(in.data(), out.data(), in.size(), out.size()); 12 | if(rv < 1) cerr << "Something went wrong!" << endl; 13 | else out.resize(rv); 14 | } 15 | 16 | void lz4_decompress(const buffer& in, buffer& out) 17 | { 18 | auto rv = LZ4_decompress_safe(in.data(), out.data(), in.size(), out.size()); 19 | if(rv < 1) cerr << "Something went wrong!" << endl; 20 | else out.resize(rv); 21 | } 22 | 23 | int main(int argc, char** argv) 24 | { 25 | buffer data(1000, 'X'); 26 | buffer compressed(data.size()), decompressed(data.size()); 27 | 28 | lz4_compress(data, compressed); 29 | cout << "LZ4 compress, bytes in: " << data.size() << ", bytes out: " << compressed.size() << endl; 30 | 31 | lz4_decompress(compressed, decompressed); 32 | cout << "LZ4 decompress, bytes in: " << compressed.size() << ", bytes out: " << decompressed.size() << endl; 33 | 34 | if(data != decompressed) cerr << "Oh snap! Data mismatch!" << endl; 35 | else cout << "Decompressed data matches original :o)" << endl; 36 | 37 | return 1; 38 | } 39 | -------------------------------------------------------------------------------- /src/otp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ansi_escape_code.hpp" 6 | 7 | using namespace std; 8 | using namespace ansi_escape_code; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | auto result = oath_init(); 13 | if(result != OATH_OK) 14 | { 15 | cerr << oath_strerror(result) << endl; 16 | return -1; 17 | } 18 | 19 | const char secret[] = "00112233445566778899"; 20 | const auto time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; 21 | const auto digits = 6; 22 | 23 | while(true) 24 | { 25 | auto t = time(NULL); 26 | auto left = time_step - (t % time_step); 27 | 28 | char otp[digits + 1] = {}; 29 | result = oath_totp_generate( 30 | secret, 31 | sizeof(secret), 32 | t, 33 | time_step, 34 | OATH_TOTP_DEFAULT_START_TIME, 35 | digits, 36 | otp); 37 | 38 | if(result != OATH_OK) 39 | { 40 | cerr << oath_strerror(result) << endl; 41 | return -1; 42 | } 43 | 44 | cout << bold << "OTP: " << reset 45 | << bright_red << otp << reset 46 | << slow_blink << " (" << left << ")" << reset; 47 | cout.flush(); 48 | 49 | this_thread::sleep_for(1s); 50 | cout << "\r"; 51 | } 52 | 53 | oath_done(); 54 | 55 | return 1; 56 | } 57 | -------------------------------------------------------------------------------- /src/grpc_service_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "grpc_service.grpc.pb.h" 8 | 9 | using namespace std; 10 | using namespace grpc; 11 | 12 | class gRPCClient 13 | { 14 | public: 15 | gRPCClient(shared_ptr channel) 16 | : stub(gRPCService::NewStub(channel)) {} 17 | 18 | string hello(const std::string& msg) 19 | { 20 | Input input; 21 | Output output; 22 | ClientContext context; 23 | 24 | input.set_input_msg(msg); 25 | 26 | Status status = stub->hello(&context, input, &output); 27 | if (status.ok()) 28 | { 29 | return output.output_msg(); 30 | } 31 | else 32 | { 33 | cout << status.error_code() << ": " << status.error_message() << endl; 34 | return "RPC failed"; 35 | } 36 | } 37 | 38 | private: 39 | unique_ptr stub; 40 | }; 41 | 42 | int main(int argc, char** argv) 43 | { 44 | gRPCClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials())); 45 | string reply = client.hello("gRPC client says hi!"); 46 | cout << "Client received: " << reply << endl; 47 | 48 | return 1; 49 | } 50 | -------------------------------------------------------------------------------- /src/bad_pointer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct bad 7 | { 8 | bad() : p1(new int), p2(new int) 9 | { 10 | *p1 = 1; 11 | *p2 = 2; 12 | } 13 | 14 | ~bad() 15 | { 16 | delete p1; 17 | delete p2; 18 | } 19 | 20 | int* p1; 21 | int* p2; 22 | }; 23 | 24 | struct still_bad 25 | { 26 | still_bad() try : p1(new int), p2(new int) 27 | { 28 | *p1 = 1; 29 | *p2 = 2; 30 | } 31 | catch(...) 32 | { 33 | // if(p1) delete p1; // ILLEGAL~!!! 34 | // if(p2) delete p2; // ILLEGAL~!!! 35 | /* 36 | * From: https://en.cppreference.com/w/cpp/language/function-try-block 37 | * 38 | * The behavior is undefined if the catch-clause of a function-try-block used on a 39 | * constructor or a destructor accesses a base or a non-static member of the object. 40 | */ 41 | } 42 | 43 | ~still_bad() 44 | { 45 | delete p1; 46 | delete p2; 47 | } 48 | 49 | int* p1 = nullptr; 50 | int* p2 = nullptr; 51 | }; 52 | 53 | struct good 54 | { 55 | good() : p1(make_unique()), p2(make_unique()) 56 | { 57 | *p1 = 1; 58 | *p2 = 2; 59 | } 60 | 61 | unique_ptr p1; 62 | unique_ptr p2; 63 | }; 64 | 65 | int main() 66 | { 67 | bad b; 68 | still_bad sb; 69 | good g; 70 | } 71 | -------------------------------------------------------------------------------- /src/file_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | const int READ_SIZE = 1024 * 1024; 11 | 12 | int main(int argc, char** argv) 13 | { 14 | if(argc != 2) 15 | { 16 | cerr << "USAGE: " << argv[0] << " " << endl; 17 | return -1; 18 | } 19 | 20 | try 21 | { 22 | using Buffer = vector; 23 | Buffer buffer(READ_SIZE); 24 | 25 | auto sha1 = Botan::HashFunction::create_or_throw("SHA-1"); 26 | 27 | ifstream ifs; 28 | 29 | ifs.exceptions(ios::failbit | ios::badbit); 30 | ifs.open(argv[1]); 31 | ifs.seekg(0, ios_base::end); 32 | size_t bytes_left = ifs.tellg(); 33 | ifs.seekg(ios_base::beg); 34 | 35 | while(bytes_left) 36 | { 37 | size_t bytes_to_read = bytes_left > READ_SIZE ? READ_SIZE : bytes_left; 38 | 39 | buffer.resize(bytes_to_read); 40 | ifs.read(buffer.data(), buffer.size()); 41 | 42 | sha1->update((uint8_t*)buffer.data(), buffer.size()); 43 | 44 | bytes_left -= bytes_to_read; 45 | } 46 | 47 | ifs.close(); 48 | 49 | cout << Botan::hex_encode(sha1->final()) << " " << argv[1] << endl; 50 | } 51 | catch(exception& e) 52 | { 53 | cerr << e.what() << endl; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/membars.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | //#define ATOMIC_FENCE 8 | //#define ATOMIC_RELEASE 9 | //#define ATOMIC_CONSUME 10 | 11 | #if defined ATOMIC_FENCE 12 | #define FENCE_ACQUIRE atomic_thread_fence(memory_order_acquire) 13 | #define FENCE_RELEASE atomic_thread_fence(memory_order_release) 14 | #elif defined ATOMIC_RELEASE 15 | atomic_bool f{false}; 16 | #define FENCE_ACQUIRE f.load(memory_order_acquire) 17 | #define FENCE_RELEASE f.store(true, memory_order_release) 18 | #elif defined ATOMIC_CONSUME 19 | atomic_bool f{false}; 20 | #define FENCE_ACQUIRE f.load(memory_order_consume) 21 | #define FENCE_RELEASE f.store(flag, memory_order_release) 22 | #else 23 | #define FENCE_ACQUIRE 24 | #define FENCE_RELEASE 25 | #endif 26 | 27 | int main(int argc, char** argv) 28 | { 29 | bool flag = false; 30 | 31 | thread t1([&]() { 32 | this_thread::sleep_for(100ms); 33 | cout << "t1 started" << endl; 34 | flag = true; 35 | FENCE_RELEASE; 36 | cout << "t1 signals and exits" << endl; 37 | }); 38 | 39 | thread t2([&]() { 40 | cout << "t2 started" << endl; 41 | while(flag == false) FENCE_ACQUIRE; 42 | cout << "t2 got signaled and exits" << endl; 43 | }); 44 | 45 | t1.join(); 46 | t2.join(); 47 | 48 | return 1; 49 | } 50 | -------------------------------------------------------------------------------- /src/echo_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "event.hpp" 5 | #include "socket.hpp" 6 | 7 | int main(int argc, char** argv) 8 | { 9 | using namespace std; 10 | 11 | if(argc != 3) 12 | { 13 | cerr << "USAGE: " << argv[0] << " [host name] [port]" << endl; 14 | return 1; 15 | } 16 | 17 | try 18 | { 19 | auto host = argv[1]; 20 | auto port = stoul(argv[2]); 21 | auto event = auto_event(false); 22 | auto client = client_socket(host, port); 23 | 24 | client.set_data_handler([&](client_socket& cs, socket_buffer_t data) 25 | { 26 | auto msg = string((const char*)data.data(), data.size()); 27 | if(!msg.empty()) cout << "< " << msg << endl; 28 | event.signal(); 29 | }); 30 | 31 | thread([&]() 32 | { 33 | cout << "[enter] to send, ['q'] to exit, ['die'] to stop server" << endl; 34 | 35 | while(true) 36 | { 37 | cout << "> "; 38 | auto line = string(); 39 | getline(cin, line); 40 | 41 | if(line.empty()) 42 | continue; 43 | 44 | if(line == "q") 45 | { 46 | client.close(); 47 | break; 48 | } 49 | 50 | client.send(line.data(), line.length()); 51 | event.wait(); 52 | } 53 | }).detach(); 54 | 55 | while(client.receive()); 56 | } 57 | catch(std::exception& ex) 58 | { 59 | cerr << ex.what() << endl; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/hash.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static_assert( 13 | ((SIZE_MAX == 0xFFFFFFFF) || (SIZE_MAX == 0xFFFFFFFFFFFFFFFF)) && 14 | ((sizeof(std::size_t) * CHAR_BIT) == 32) || ((sizeof(std::size_t) * CHAR_BIT) == 64), 15 | "std::size_t is neither 32 nor 64 bit"); 16 | 17 | #if SIZE_MAX == 0xFFFFFFFF 18 | using random_generator_bits_t = std::mt19937; 19 | #elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF 20 | using random_generator_bits_t = std::mt19937_64; 21 | #else 22 | #error "std::size_t is neither 32 nor 64 bit" 23 | #endif 24 | 25 | template 26 | inline auto hashN(const K& key, std::size_t N) 27 | { 28 | if(!N) throw std::invalid_argument("Hash count must be greater than zero!"); 29 | random_generator_bits_t rng(std::hash{}(key)); 30 | std::vector hashes(N); 31 | std::generate(std::begin(hashes), std::end(hashes), rng); 32 | return hashes; 33 | } 34 | 35 | template 36 | inline auto hashN(const K& key) noexcept 37 | { 38 | static_assert(N > 0, "Hash count must be greater than zero!"); 39 | random_generator_bits_t rng(std::hash{}(key)); 40 | std::array hashes{}; 41 | std::generate(std::begin(hashes), std::end(hashes), rng); 42 | return hashes; 43 | } 44 | -------------------------------------------------------------------------------- /src/geoip.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define BOOST_BIND_GLOBAL_PLACEHOLDERS 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | int main(int argc, char** argv) 14 | { 15 | if(argc < 2) 16 | { 17 | cerr << "USAGE: geoip [hostname/IP]..." << endl; 18 | return 1; 19 | } 20 | 21 | try 22 | { 23 | curlpp::Cleanup cURLppStartStop; 24 | 25 | for(int arg = 1; arg < argc; ++arg) 26 | { 27 | auto host = string(argv[arg]); 28 | 29 | curlpp::Easy request; 30 | std::stringstream response; 31 | 32 | request.setOpt(curlpp::options::Verbose(false)); 33 | request.setOpt(curlpp::options::WriteStream(&response)); 34 | request.setOpt(curlpp::options::Url("http://ip-api.com/json/"s + host)); 35 | request.setOpt(curlpp::options::Port(80)); 36 | 37 | request.perform(); 38 | 39 | boost::property_tree::ptree data; 40 | boost::property_tree::read_json(response, data); 41 | 42 | // cout << host << "\t"; 43 | // cout << "host = " << host << endl; 44 | for(auto& it : data) 45 | cout << it.second.data() << "\t"; 46 | // cout << it.first << " = " << it.second.data() << endl; 47 | cout << endl << endl; 48 | } 49 | } 50 | catch(exception& e) 51 | { 52 | cerr << e.what() << endl; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cmake/FindBenchmark.cmake: -------------------------------------------------------------------------------- 1 | # Findbenchmark.cmake 2 | # - Try to find benchmark 3 | # 4 | # The following variables are optionally searched for defaults 5 | # benchmark_ROOT_DIR: Base directory where all benchmark components are found 6 | # 7 | # Once done this will define 8 | # benchmark_FOUND - System has benchmark 9 | # benchmark_INCLUDE_DIRS - The benchmark include directories 10 | # benchmark_LIBRARIES - The libraries needed to use benchmark 11 | 12 | set(benchmark_ROOT_DIR "" CACHE PATH "Folder containing benchmark") 13 | 14 | find_path(benchmark_INCLUDE_DIR "benchmark/benchmark.h" 15 | PATHS ${benchmark_ROOT_DIR} 16 | PATH_SUFFIXES include 17 | NO_DEFAULT_PATH) 18 | find_path(benchmark_INCLUDE_DIR "benchmark/benchmark.h") 19 | 20 | find_library(benchmark_LIBRARY NAMES "benchmark" 21 | PATHS ${benchmark_ROOT_DIR} 22 | PATH_SUFFIXES lib lib64 23 | NO_DEFAULT_PATH) 24 | find_library(benchmark_LIBRARY NAMES "benchmark") 25 | 26 | include(FindPackageHandleStandardArgs) 27 | # handle the QUIETLY and REQUIRED arguments and set benchmark_FOUND to TRUE 28 | # if all listed variables are TRUE 29 | find_package_handle_standard_args(benchmark FOUND_VAR benchmark_FOUND 30 | REQUIRED_VARS benchmark_LIBRARY 31 | benchmark_INCLUDE_DIR) 32 | 33 | if(benchmark_FOUND) 34 | set(benchmark_LIBRARIES ${benchmark_LIBRARY}) 35 | set(benchmark_INCLUDE_DIRS ${benchmark_INCLUDE_DIR}) 36 | endif() 37 | 38 | mark_as_advanced(benchmark_INCLUDE_DIR benchmark_LIBRARY) 39 | -------------------------------------------------------------------------------- /src/echo_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "socket.hpp" 6 | 7 | int main(int argc, char** argv) 8 | { 9 | using namespace std; 10 | 11 | if(argc != 2) 12 | { 13 | cerr << "USAGE: " << argv[0] << " [port]" << endl; 14 | return 1; 15 | } 16 | 17 | try 18 | { 19 | auto port = uint16_t(stoul(argv[1])); 20 | auto server = server_socket(port); 21 | 22 | server.set_accept_handler( 23 | [&](server_socket& ss, client_socket cs, host_info_t info) 24 | { 25 | cout << info.host << " (" << info.ip << ") : " << info.port << " connected" << endl; 26 | 27 | cs.set_data_handler([=, sref = ref(server)](client_socket& cs, socket_buffer_t data) 28 | { 29 | auto msg = string((const char*)data.data(), data.size()); 30 | if(msg == "die") 31 | { 32 | sref.get().close(); 33 | } 34 | else if(!msg.empty()) 35 | { 36 | cout << info.host << " > " << msg << endl; 37 | cs.send({ rbegin(data), rend(data) }); 38 | } 39 | }); 40 | 41 | thread([=, cs = std::move(cs)]() mutable 42 | { 43 | while(cs.receive()); 44 | cout << info.host << " (" << info.ip << ") : " << info.port << " disconnected" << endl; 45 | }).detach(); 46 | }); 47 | 48 | cout << "[ctrl-c] to exit, listening on port " << port << endl; 49 | 50 | while(server.accept()); 51 | } 52 | catch(std::exception& ex) 53 | { 54 | cerr << ex.what() << endl; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/synchronized.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "synchronized.hpp" 6 | 7 | struct S 8 | { 9 | void method() { std::cout << "non const\n"; } 10 | void method() const { std::cout << "const\n"; } 11 | }; 12 | 13 | int main() 14 | { 15 | shared_synchronized s1; 16 | 17 | // takes exclusive lock 18 | s1->method(); 19 | 20 | // takes shared lock 21 | std::as_const(s1)->method(); 22 | 23 | // due to const'ness calling any method on s2, s3, or s4 24 | // will ALWAYS take shared lock; no need for std::as_const 25 | const shared_synchronized s2; 26 | shared_synchronized s3; 27 | const shared_synchronized s4; 28 | 29 | // all 3 calls take shared lock 30 | s2->method(); 31 | s3->method(); 32 | s4->method(); 33 | 34 | // std::vector shared by 3 threads t1, t2, and t3 35 | // t1 and t2 take exclusive locks, t3 takes shared lock 36 | shared_synchronized> sv; 37 | 38 | std::thread t1([&] () 39 | { 40 | std::srand(std::time(NULL)); 41 | while(true) 42 | // takes exclusive lock 43 | sv->push_back(rand()); 44 | }); 45 | 46 | std::thread t2([&] () 47 | { 48 | while(true) 49 | // takes exclusive lock 50 | sv->clear(); 51 | }); 52 | 53 | std::thread t3([&] () 54 | { 55 | while(true) 56 | // takes shared lock 57 | [[maybe_unused]] auto _ = std::as_const(sv)->empty(); 58 | }); 59 | 60 | t1.join(); 61 | t2.join(); 62 | t3.join(); 63 | } 64 | -------------------------------------------------------------------------------- /src/new.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct T 6 | { 7 | T() { std::cout << "T() @ " << std::hex << std::showbase << this << std::endl; } 8 | ~T() { std::cout << "~T() @ " << std::hex << std::showbase << this << std::endl; } 9 | }; 10 | 11 | int main(int argc, char** argv) 12 | { 13 | // new 14 | // this: 15 | T* t1 = new T; 16 | // becomes this: 17 | T* t2 = (T*)operator new(sizeof(T)); 18 | try 19 | { 20 | new (t2) T; 21 | } 22 | catch(...) 23 | { 24 | operator delete(t2); 25 | throw; 26 | } 27 | 28 | // delete 29 | // this: 30 | delete t1; 31 | // becomes this: 32 | t2->~T(); 33 | operator delete(t2); 34 | 35 | // new [] 36 | // this: 37 | #define HOW_MANY 3 38 | T* t3 = new T[HOW_MANY]; 39 | // becomes this: 40 | T* t4 = (T*)operator new(sizeof(size_t) + HOW_MANY * sizeof(T)); 41 | *((size_t*)t4) = HOW_MANY; 42 | t4 = (T*)(((char*)t4) + sizeof(size_t)); 43 | for(size_t i = 0; i < HOW_MANY; ++i) 44 | { 45 | try 46 | { 47 | new (t4 + i) T; 48 | } 49 | catch(...) 50 | { 51 | for(size_t i2 = 0; i2 < i; ++i2) 52 | t4[i2].~T(); 53 | t4 = (T*)(((char*)t4) - sizeof(size_t)); 54 | operator delete(t4); 55 | throw; 56 | } 57 | } 58 | 59 | // delete [] 60 | // this: 61 | delete [] t3; 62 | // becomes: 63 | size_t how_many = *(size_t*)(((char*)t4) - sizeof(size_t)); 64 | for(size_t i = 0; i < how_many; ++i) 65 | t4[i].~T(); 66 | t4 = (T*)(((char*)t4) - sizeof(size_t)); 67 | operator delete(t4); 68 | 69 | return 1; 70 | } 71 | -------------------------------------------------------------------------------- /src/timer_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "timer.hpp" 4 | 5 | using namespace std; 6 | using namespace std::chrono; 7 | 8 | int main() 9 | { 10 | auto start = high_resolution_clock::now(); 11 | auto duration = [start]() 12 | { 13 | auto now = high_resolution_clock::now(); 14 | auto msecs = duration_cast(now - start).count(); 15 | stringstream ss; 16 | ss << msecs / 1000.0; 17 | cout << "elapsed " << ss.str() << "s\t: "; 18 | }; 19 | 20 | timer t(10ms); 21 | 22 | auto t0 = t.set_timeout(1ms, [&]() { duration(); cout << "start timeout" << endl; }); 23 | auto t1 = t.set_timeout( 1s, [&]() { duration(); cout << "timeout 1s" << endl; }); 24 | auto t2 = t.set_timeout( 2s, [&]() { duration(); cout << "timeout 2s" << endl; }); 25 | auto t3 = t.set_timeout( 3s, [&]() { duration(); cout << "timeout 3s" << endl; }); 26 | auto t4 = t.set_timeout( 5s, [&]() { duration(); cout << "timeout that never happens" << endl; }); 27 | 28 | auto i1 = t.set_interval(1s, [&]() { duration(); cout << "interval 1s" << endl; }); 29 | auto i2 = t.set_interval(2s, [&]() { duration(); cout << "interval 2s" << endl; }); 30 | auto i3 = t.set_interval(3s, [&]() { duration(); cout << "interval 3s" << endl; }); 31 | auto i4 = t.set_interval(5s, [&]() { duration(); cout << "interval that never happens" << endl; }); 32 | 33 | auto tf = t.set_timeout(10s, [&]() { duration(); cout << "end timeout" << endl; }); 34 | 35 | t0->wait(); 36 | t4->cancel(); 37 | i4->cancel(); 38 | tf->wait_for(11s); 39 | } 40 | -------------------------------------------------------------------------------- /src/options.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | using namespace boost::program_options; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | int v{}; 12 | float f{}; 13 | string s{}; 14 | vector vi{}; 15 | vector vs{}; 16 | 17 | options_description desc("Allowed options"); 18 | desc.add_options() 19 | ("help", "produce help message") 20 | ("int,i", value(&v)->default_value(42), "int value") 21 | ("float,f", value(&f)->default_value(3.141f), "float value") 22 | ("string,s", value(&s)->default_value("Vorbrodt"), "string value") 23 | ("int_list,a", value>(&vi), "list of int values") 24 | ("string_list,b", value>(&vs), "list of string values"); 25 | 26 | variables_map vm{}; 27 | store(parse_command_line(argc, argv, desc), vm); 28 | notify(vm); 29 | 30 | if (vm.size() == 0 || vm.count("help")) 31 | { 32 | cout << desc << "\n"; 33 | return 1; 34 | } 35 | 36 | if(vm.count("int")) cout << "Int value was set to " << v << endl; 37 | if(vm.count("float")) cout << "Float value was set to " << f << endl; 38 | if(vm.count("string")) cout << "String value was set to \"" << s << "\"" << endl; 39 | if(vm.count("int_list")) 40 | for(auto it : vi) 41 | cout << "List of ints value was set to " << it << endl; 42 | if(vm.count("string_list")) 43 | for(auto& it : vs) 44 | cout << "List of strings value was set to \"" << it << "\"" << endl; 45 | 46 | return 1; 47 | } 48 | -------------------------------------------------------------------------------- /src/xml.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace boost::archive; 11 | using namespace boost::serialization; 12 | 13 | struct person 14 | { 15 | string name; 16 | int dob; 17 | string email; 18 | }; 19 | 20 | using people = vector; 21 | 22 | BOOST_CLASS_VERSION(person, 1); 23 | BOOST_CLASS_VERSION(people, 1); 24 | 25 | template 26 | void serialize(Archive& ar, person& person, const unsigned int version) 27 | { 28 | ar & BOOST_SERIALIZATION_NVP(person.name); 29 | ar & BOOST_SERIALIZATION_NVP(person.dob); 30 | ar & BOOST_SERIALIZATION_NVP(person.email); 31 | } 32 | 33 | int main(int argc, char** argv) 34 | { 35 | person me{"Martin Vorbrodt", 19800830, "martin@vorbrodt.blog"}; 36 | person her{"Dorota Vorbrodt", 19810127, "dorota@vorbrodt.blog"}; 37 | people us{me, her}; 38 | { 39 | ofstream ofs("data.xml"); 40 | xml_oarchive oa(ofs, boost::archive::no_header); 41 | oa << make_nvp("people", us); 42 | } 43 | 44 | people us_too{}; 45 | { 46 | ifstream ifs("data.xml"); 47 | xml_iarchive ia(ifs, boost::archive::no_header); 48 | ia >> make_nvp("people", us_too); 49 | } 50 | 51 | for(auto& p : us_too) 52 | { 53 | cout << "Name : " << p.name << endl; 54 | cout << "DOB : " << p.dob << endl; 55 | cout << "EMail : " << p.email << endl << endl; 56 | } 57 | 58 | return 1; 59 | } 60 | -------------------------------------------------------------------------------- /src/throttle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "token_bucket.hpp" 6 | 7 | #define all(c) for(auto& it : c) it 8 | 9 | int main() 10 | { 11 | using namespace std; 12 | using namespace std::chrono; 13 | 14 | try 15 | { 16 | auto N = 1; 17 | auto bucket = token_bucket(1ms, 1'000, true); 18 | auto count = thread::hardware_concurrency() - 1; 19 | auto run = atomic_bool{ true }; 20 | auto total = atomic_uint64_t{}; 21 | auto counts = vector(count); 22 | auto fair_start = latch(count + 1); 23 | auto threads = vector(count); 24 | 25 | thread([&] 26 | { 27 | fair_start.arrive_and_wait(); 28 | 29 | auto start = steady_clock::now(); 30 | auto sec = 0; 31 | 32 | while (run) 33 | { 34 | auto cnt = 1; 35 | for (auto& count : counts) 36 | cout << fixed << "Cnt " << cnt++ << ":\t" << count << "\t / \t" << (100.0 * count / total) << " % \n"; 37 | 38 | cout << "Total:\t" << total << "\nTime:\t" << duration_cast(steady_clock::now() - start).count() << "s\n" << endl; 39 | 40 | this_thread::sleep_until(start + seconds(++sec)); 41 | } 42 | }).detach(); 43 | 44 | auto worker = [&](auto x) 45 | { 46 | fair_start.arrive_and_wait(); 47 | 48 | while (run) 49 | { 50 | bucket.consume(N); 51 | total += N; 52 | counts[x] += N; 53 | } 54 | }; 55 | 56 | all(threads) = thread(worker, --count); 57 | 58 | cin.get(); 59 | run = false; 60 | all(threads).join(); 61 | } 62 | catch (exception& ex) 63 | { 64 | cerr << ex.what() << endl; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/deadlock.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace chrono; 11 | 12 | void DO_SOME_WORK(const char* msg) 13 | { 14 | { 15 | static mutex cout_lock; 16 | auto t = system_clock::to_time_t(system_clock::now()); 17 | lock_guard guard(cout_lock); 18 | cout << msg << " @ " << ctime(&t); 19 | } 20 | this_thread::sleep_for(milliseconds(rand() % 10)); 21 | } 22 | 23 | void BAD() 24 | { 25 | mutex m1, m2; 26 | 27 | thread t1([&]() 28 | { 29 | while(true) 30 | { 31 | m1.lock(); 32 | 33 | DO_SOME_WORK("Thread 1"); 34 | 35 | m2.lock(); 36 | 37 | DO_SOME_WORK("Thread 1"); 38 | 39 | m1.unlock(); 40 | m2.unlock(); 41 | } 42 | }); 43 | 44 | thread t2([&]() 45 | { 46 | while(true) 47 | { 48 | m2.lock(); 49 | 50 | DO_SOME_WORK("Thread 2"); 51 | 52 | m1.lock(); 53 | 54 | DO_SOME_WORK("Thread 2"); 55 | 56 | m1.unlock(); 57 | m2.unlock(); 58 | } 59 | }); 60 | 61 | t1.join(); 62 | t2.join(); 63 | } 64 | 65 | void GOOD() 66 | { 67 | mutex m1, m2; 68 | 69 | thread t1([&]() 70 | { 71 | while(true) 72 | { 73 | scoped_lock guard(m1, m2); 74 | 75 | DO_SOME_WORK("Thread 1"); 76 | } 77 | }); 78 | 79 | thread t2([&]() 80 | { 81 | while(true) 82 | { 83 | scoped_lock guard(m2, m1); 84 | 85 | DO_SOME_WORK("Thread 2"); 86 | } 87 | }); 88 | 89 | t1.join(); 90 | t2.join(); 91 | } 92 | 93 | int main() 94 | { 95 | srand((unsigned int)time(NULL)); 96 | //BAD(); 97 | GOOD(); 98 | } 99 | -------------------------------------------------------------------------------- /src/interview_6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | mutex cout_lock; 10 | #define trace(x) { scoped_lock lock(cout_lock); cout << x << endl; } 11 | 12 | const int COUNT = 10; 13 | 14 | struct range 15 | { 16 | unsigned int lo; 17 | unsigned int hi; 18 | 19 | bool in_range(unsigned int p) const { return lo <= p && p < hi; } 20 | }; 21 | 22 | bool operator < (const range& lhs, const range& rhs) 23 | { 24 | return lhs.lo < rhs.lo; 25 | } 26 | 27 | ostream& operator << (ostream& os, const range& r) 28 | { 29 | os << "LO: " << r.lo << ", HI: " << r.hi; 30 | return os; 31 | } 32 | 33 | range BinarySearch(const vector& v, unsigned int p) 34 | { 35 | size_t index = v.size() / 2; 36 | size_t step = index / 2 + 1; 37 | 38 | while(true) 39 | { 40 | if(v[index].hi <= p) index += step; 41 | if(v[index].lo > p) index -= step; 42 | step /= 2; 43 | if(step == 0) step = 1; 44 | if(v[index].in_range(p)) break; 45 | } 46 | 47 | return v[index]; 48 | } 49 | 50 | int main(int argc, char** argv) 51 | { 52 | srand((unsigned int)time(NULL)); 53 | 54 | vector ranges = 55 | { 56 | {50, 60}, {60, 70}, {70, 80}, {80, 90}, {90, 100}, 57 | {0, 10}, {10, 20}, {20, 30}, {30, 40}, {40, 50} 58 | }; 59 | 60 | sort(begin(ranges), end(ranges)); 61 | 62 | for(const auto& r : ranges) 63 | trace(r); 64 | 65 | for(int i = 0; i < COUNT; ++i) 66 | { 67 | unsigned int p = rand() % 100; 68 | trace("P = " << p << " falls in range " << BinarySearch(ranges, p)); 69 | } 70 | 71 | return 1; 72 | } 73 | -------------------------------------------------------------------------------- /src/newtrace.txt: -------------------------------------------------------------------------------- 1 | This code REQUIRES AT LEAST C++17 2 | 3 | Include the following 2 lines in every h/cpp file in your project: 4 | 5 | #define ENABLE_NEW_DELETE_TRACE_DUMP 6 | #include "newtrace.hpp" 7 | 8 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 9 | !!! PUT CODE LINES RIGHT AFTER YOUR LAST #include STATEMENT !!! 10 | !!! AND BEFORE ANY CODE THAT CALLS NEW OR DELETE OPERATORS !!! 11 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | 13 | Defining ENABLE_NEW_DELETE_TRACE_DUMP will produce leak/mismatch 14 | printout when program exits. Alternatively you can include only 15 | "newtrace.hpp" then issue one of the following calls: 16 | 17 | 1) ndt::dump_leak(); // to print leaks detected so far 18 | 2) ndt::dump_mismatch(); // to print mismatches detected so far 19 | 3) ndt::dump_all(); // #1 followed by #2 20 | 21 | This tracing works with new/new[] as well as delete/delete[] calls 22 | and properly detects mismatch cases: memory allocated with new[] 23 | is released with delete, and vice versa. Both cases are leaks at 24 | best and crashed or heap corruption at worse! 25 | 26 | This tracing works correctly in the presence of exceptions coming 27 | from constructors after memory has be successfully allocated; 28 | such cases are not memory leaks so don't look for traces of it... 29 | 30 | This tracing is NOT COMPATIBLE with: 31 | 1) Placement new operator: new ( ptr ) T; 32 | 2) nothrow new operator: new ( std::nothrow ) T; 33 | Those will produce a compile time error; to use placement/nothrow 34 | new you must first #undef new. 35 | 36 | 37 | 38 | Thou Shalt Not Leak. 39 | -V 40 | -------------------------------------------------------------------------------- /cmake/FindBotan.cmake: -------------------------------------------------------------------------------- 1 | # - Find botan 2 | # Find the botan cryptographic library 3 | # 4 | # This module defines the following variables: 5 | # BOTAN_FOUND - True if library and include directory are found 6 | # If set to TRUE, the following are also defined: 7 | # BOTAN_INCLUDE_DIRS - The directory where to find the header file 8 | # BOTAN_LIBRARIES - Where to find the library file 9 | # 10 | # For conveniance, these variables are also set. They have the same values 11 | # than the variables above. The user can thus choose his/her prefered way 12 | # to write them. 13 | # BOTAN_LIBRARY 14 | # BOTAN_INCLUDE_DIR 15 | # 16 | # This file is in the public domain 17 | 18 | # include(FindPkgConfig) 19 | 20 | pkg_check_modules(BOTAN botan-2) 21 | pkg_check_modules(BOTAN botan-1.11) 22 | 23 | if(NOT BOTAN_FOUND) 24 | find_path(BOTAN_INCLUDE_DIRS NAMES botan/botan.h 25 | PATH_SUFFIXES botan-2 botan-1.11 26 | DOC "The botan include directory") 27 | 28 | find_library(BOTAN_LIBRARIES NAMES botan botan-2 botan-1.11 29 | DOC "The botan library") 30 | 31 | # Use some standard module to handle the QUIETLY and REQUIRED arguments, and 32 | # set BOTAN_FOUND to TRUE if these two variables are set. 33 | include(FindPackageHandleStandardArgs) 34 | find_package_handle_standard_args(BOTAN REQUIRED_VARS BOTAN_LIBRARIES BOTAN_INCLUDE_DIRS) 35 | 36 | if(BOTAN_FOUND) 37 | set(BOTAN_LIBRARY ${BOTAN_LIBRARIES} CACHE INTERNAL "") 38 | set(BOTAN_INCLUDE_DIR ${BOTAN_INCLUDE_DIRS} CACHE INTERNAL "") 39 | set(BOTAN_FOUND ${BOTAN_FOUND} CACHE INTERNAL "") 40 | endif() 41 | endif() 42 | 43 | mark_as_advanced(BOTAN_INCLUDE_DIRS BOTAN_LIBRARIES) 44 | 45 | -------------------------------------------------------------------------------- /src/sorting.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace chrono; 12 | 13 | const int ELEMS = 10'000'000; 14 | 15 | int main(int argc, char** argv) 16 | { 17 | srand((unsigned int)time(NULL)); 18 | 19 | using Container1 = list; 20 | Container1 c; 21 | c.resize(ELEMS); 22 | generate(begin(c), end(c), rand); 23 | 24 | auto start_time = high_resolution_clock::now(); 25 | accumulate(begin(c), end(c), 0); 26 | cout << fixed << setprecision(3); 27 | cout << "List duration " << duration_cast(high_resolution_clock::now() - start_time).count() / 1000.f << " ms" << endl; 28 | 29 | c.sort(); 30 | 31 | start_time = high_resolution_clock::now(); 32 | accumulate(begin(c), end(c), 0); 33 | cout << "Sorted list duration " << duration_cast(high_resolution_clock::now() - start_time).count() / 1000.f << " ms" << endl; 34 | c.clear(); 35 | 36 | using Container2 = vector; 37 | Container2 c2; 38 | c2.resize(ELEMS); 39 | generate(begin(c2), end(c2), rand); 40 | 41 | start_time = high_resolution_clock::now(); 42 | accumulate(begin(c2), end(c2), 0); 43 | cout << "Vector duration " << duration_cast(high_resolution_clock::now() - start_time).count() / 1000.f << " ms" << endl; 44 | 45 | sort(begin(c2), end(c2)); 46 | 47 | start_time = high_resolution_clock::now(); 48 | accumulate(begin(c2), end(c2), 0); 49 | cout << "Sorted vector duration " << duration_cast(high_resolution_clock::now() - start_time).count() / 1000.f << " ms" << endl << endl; 50 | } 51 | -------------------------------------------------------------------------------- /src/bloom.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "hash.hpp" 10 | 11 | template 12 | class bloom_filter 13 | { 14 | public: 15 | explicit bloom_filter(std::size_t size, std::size_t hash_count = 3) 16 | : m_bits(size), m_hash_count(hash_count) 17 | { 18 | if(!size) throw std::invalid_argument("Size must be greater than zero!"); 19 | if(!hash_count) throw std::invalid_argument("Hash count must be greater than zero!"); 20 | } 21 | 22 | void add(const Key& key) noexcept 23 | { 24 | auto hs = hashN(key, m_hash_count); 25 | for(auto h : hs) 26 | m_bits[h % m_bits.size()] = true; 27 | } 28 | 29 | bool contains(const Key& key) const noexcept 30 | { 31 | auto hs = hashN(key, m_hash_count); 32 | for(auto h : hs) 33 | if(!m_bits[h % m_bits.size()]) 34 | return false; 35 | return true; 36 | } 37 | 38 | private: 39 | std::vector m_bits; 40 | std::size_t m_hash_count; 41 | }; 42 | 43 | template 44 | class fixed_bloom_filter 45 | { 46 | public: 47 | static_assert(Size > 0, "Size must be greater than zero!"); 48 | static_assert(HashCount > 0, "Hash count must be greater than zero!"); 49 | 50 | void add(const Key& key) 51 | { 52 | auto hs = hashN(key); 53 | for(auto h : hs) 54 | m_bits[h % Size] = true; 55 | } 56 | 57 | bool contains(const Key& key) const 58 | { 59 | auto hs = hashN(key); 60 | for(auto h : hs) 61 | if(!m_bits[h % Size]) 62 | return false; 63 | return true; 64 | } 65 | 66 | private: 67 | std::bitset m_bits; 68 | }; 69 | -------------------------------------------------------------------------------- /src/hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | struct H 8 | { 9 | string s1, s2; 10 | }; 11 | 12 | ostream& operator << (ostream& os, const H& h) 13 | { 14 | os << h.s1 << "," << h.s2; 15 | return os; 16 | } 17 | 18 | namespace std 19 | { 20 | template <> struct hash 21 | { 22 | size_t operator()(const H& h) const 23 | { 24 | return hash{}(h.s1) ^ (hash{}(h.s2) << 1); 25 | } 26 | }; 27 | } 28 | 29 | int main() 30 | { 31 | for(int i = 1; i <= 3; ++i) 32 | cout << "Hash of '" << i << "': " << hash{}(i) << endl; 33 | 34 | for(char c = 'A'; c <= 'C'; ++c) 35 | cout << "Hash of '" << c << "': " << hash{}(c) << endl; 36 | 37 | for(float f = 1.1f; f < 1.4f; f += 0.1f) 38 | cout << "Hash of '" << f << "': " << hash{}(f) << endl; 39 | 40 | char* p = new char[3]; 41 | char* q = p; 42 | for(; p < q + 3; ++p) 43 | cout << "Hash of '" << (int*)p << "': " << hash{}(p) << endl; 44 | 45 | string s1 = "Vorbrodt's C++ Blog"; 46 | string s2 = "Vorbrodt's C++ Blog"; 47 | string s3 = "https://vorbrodt.blog"; 48 | cout << "Hash of '" << s1 << "': " << hash{}(s1) << endl; 49 | cout << "Hash of '" << s2 << "': " << hash{}(s2) << endl; 50 | cout << "Hash of '" << s3 << "': " << hash{}(s3) << endl; 51 | 52 | H h1{"Vorbrodt's C++ Blog", "https://vorbrodt.blog"}; 53 | H h2{"Vorbrodt's C++ Blog", "https://vorbrodt.blog"}; 54 | H h3{"https://vorbrodt.blog", "Vorbrodt's C++ Blog"}; 55 | cout << "Hash of '" << h1 << "': " << hash{}(h1) << endl; 56 | cout << "Hash of '" << h2 << "': " << hash{}(h2) << endl; 57 | cout << "Hash of '" << h3 << "': " << hash{}(h3) << endl; 58 | } 59 | -------------------------------------------------------------------------------- /run_jenkins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | set -e 3 | 4 | # pull in environment variables like $PATH etc.. 5 | # 6 | source /Users/martin/.zshrc 7 | # 8 | # ------------------------------ 9 | 10 | # update brew packages used by the code-base... 11 | # 12 | brew update 13 | brew upgrade 14 | brew cleanup 15 | # 16 | # ------------------------------ 17 | 18 | # update vcpkg packages used by the code-base... 19 | # 20 | vcpkg update 21 | vcpkg upgrade 22 | vcpkg remove --outdated 23 | # 24 | # ------------------------------ 25 | 26 | # make latest installed llvm/clang the compiler of choice... 27 | # 28 | export PATH="/usr/local/bin:$PATH" 29 | export CC=/usr/local/bin/clang 30 | export CXX=/usr/local/bin/clang++ 31 | export LD=/usr/local/bin/ld.lld 32 | export AR=/usr/local/bin/llvm-ar 33 | export RANLIB=/usr/local/bin/llvm-ranlib 34 | export LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib" 35 | export CPPFLAGS="-I/usr/local/include -I/usr/local/include/c++/v1/" 36 | export CXXFLAGS="$CPPFLAGS" 37 | alias cc=$CC 38 | alias c++=$CXX 39 | alias ld=$LD 40 | alias ar=$AR 41 | alias ranlib=$RANLIB 42 | # 43 | # ------------------------------ 44 | 45 | # use llvm/clang cmake configuration... 46 | # 47 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 48 | # 49 | # ------------------------------ 50 | 51 | # use llvm/clang compatible list of cmake targets... 52 | # 53 | cd src 54 | ln -sf CMakeLists.txt.llvm CMakeLists.txt 55 | cd .. 56 | # 57 | # ------------------------------ 58 | 59 | # generate makefiles from llvm/clang cmake configuration... 60 | # 61 | rm -rf jenkins 62 | mkdir jenkins 63 | cd jenkins 64 | cmake .. 65 | # 66 | # ------------------------------ 67 | 68 | # build it~!!!!!111oneone 69 | # 70 | make 71 | cd .. 72 | # 73 | # ------------------------------ 74 | 75 | # shit be gone! 76 | # 77 | exit 0 78 | # 79 | # ------------------------------ 80 | -------------------------------------------------------------------------------- /src/unordered.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "trace.hpp" 4 | 5 | using namespace std; 6 | 7 | const int COUNT = 10'000'000; 8 | 9 | int main(int argc, char** argv) 10 | { 11 | unordered_map m; 12 | for(int i = 0; i < COUNT; ++i) 13 | m[i] = i; 14 | 15 | trace("unordered_map"); 16 | trace("initial"); 17 | trace("size = ", m.size()); 18 | trace("bucket count = ", m.bucket_count()); 19 | trace("load factor = ", m.load_factor(), "\n"); 20 | 21 | m.clear(); 22 | m.reserve(COUNT); 23 | for(int i = 0; i < COUNT; ++i) 24 | m[i] = i; 25 | 26 | trace("reserved"); 27 | trace("size = ", m.size()); 28 | trace("bucket count = ", m.bucket_count()); 29 | trace("load factor = ", m.load_factor(), "\n"); 30 | 31 | m.max_load_factor(10); 32 | m.rehash(COUNT / 10); 33 | 34 | trace("re-hashed"); 35 | trace("size = ", m.size()); 36 | trace("bucket count = ", m.bucket_count()); 37 | trace("load factor = ", m.load_factor(), "\n"); 38 | 39 | unordered_set s; 40 | for(int i = 0; i < COUNT; ++i) 41 | s.insert(i); 42 | 43 | trace("unordered_set"); 44 | trace("initial"); 45 | trace("size = ", s.size()); 46 | trace("bucket count = ", s.bucket_count()); 47 | trace("load factor = ", s.load_factor(), "\n"); 48 | 49 | s.clear(); 50 | s.reserve(COUNT); 51 | for(int i = 0; i < COUNT; ++i) 52 | s.insert(i); 53 | 54 | trace("reserved"); 55 | trace("size = ", s.size()); 56 | trace("bucket count = ", s.bucket_count()); 57 | trace("load factor = ", s.load_factor(), "\n"); 58 | 59 | s.max_load_factor(10); 60 | s.rehash(COUNT / 10); 61 | 62 | trace("re-hashed"); 63 | trace("size = ", s.size()); 64 | trace("bucket count = ", s.bucket_count()); 65 | trace("load factor = ", s.load_factor(), "\n"); 66 | 67 | return 1; 68 | } 69 | -------------------------------------------------------------------------------- /src/colors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ansi_escape_code.hpp" 3 | 4 | using namespace std; 5 | using namespace ansi_escape_code; 6 | 7 | int main() 8 | { 9 | cout << bold << "BOLD" << reset << endl; 10 | cout << faint << "FAINT" << reset << endl; 11 | cout << italic << "ITALIC" << reset << endl; 12 | cout << underline << "UNDERLINE" << reset << endl; 13 | cout << slow_blink << "SLOW BLINK" << reset << endl; 14 | cout << inverse << "INVERSE" << reset << endl << endl; 15 | 16 | for(int n = 0; n <= 255; ++n) 17 | cout << color_n(n) << bold << "X" << reset << color_bg_n(n) << bold << "X" << reset; 18 | cout << endl << endl; 19 | 20 | #ifndef __APPLE__ 21 | for(int r = 0; r <= 255; r+=32) 22 | for(int g = 0; g <= 255; g+=32) 23 | for(int b = 0; b <= 255; b+=32) 24 | cout << color_rgb(r, g, b) << bold << "X" << reset << color_bg_rgb(r, g, b) << bold << "X" << reset; 25 | cout << reset < 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | 13 | template 14 | inline auto make_vector(std::size_t N, Args&&... args) 15 | { 16 | std::vector result; 17 | result.reserve(N); 18 | 19 | while(N--) 20 | result.emplace_back(std::forward(args)...); 21 | 22 | return result; 23 | } 24 | 25 | template 26 | inline auto make_vector(std::size_t N, std::initializer_list l) 27 | { 28 | std::vector result; 29 | result.reserve(N); 30 | 31 | while(N--) 32 | result.emplace_back(l); 33 | 34 | return result; 35 | } 36 | 37 | 38 | 39 | template 40 | struct basic_stack_timer 41 | { 42 | using StrT = std::add_pointer_t>; 43 | 44 | explicit basic_stack_timer(StrT prefix = StrT("duration: "), StrT postfix = StrT(" s"), std::basic_ostream& output = std::cout) 45 | : m_prefix{ prefix }, m_postfix{ postfix }, m_output{ output } {} 46 | 47 | ~basic_stack_timer() 48 | { 49 | auto end_time = std::chrono::high_resolution_clock::now(); 50 | 51 | using duration_t = std::chrono::duration; 52 | auto duration = std::chrono::duration_cast(end_time - m_start_time); 53 | 54 | m_output << m_prefix << duration.count() << m_postfix << std::endl; 55 | } 56 | 57 | void* operator new (std::size_t) = delete; 58 | void* operator new [] (std::size_t) = delete; 59 | 60 | private: 61 | StrT m_prefix; 62 | StrT m_postfix; 63 | std::basic_ostream& m_output; 64 | std::chrono::high_resolution_clock::time_point m_start_time = std::chrono::high_resolution_clock::now(); 65 | }; 66 | 67 | using stack_timer = basic_stack_timer; 68 | //using stack_timer = basic_stack_timer; 69 | 70 | -------------------------------------------------------------------------------- /src/semaphore.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * fast_semaphore designed by Joe Seigh, implemented by Chris Thomasson 3 | * 4 | * https://www.haiku-os.org/legacy-docs/benewsletter/Issue1-26.html 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class semaphore 15 | { 16 | public: 17 | explicit semaphore(unsigned int count = 0) 18 | : m_count(count) {} 19 | 20 | void post() 21 | { 22 | { 23 | std::unique_lock lock(m_mutex); 24 | ++m_count; 25 | } 26 | m_cv.notify_one(); 27 | } 28 | 29 | void post(unsigned int count) 30 | { 31 | { 32 | std::unique_lock lock(m_mutex); 33 | m_count += count; 34 | } 35 | m_cv.notify_all(); 36 | } 37 | 38 | void wait() 39 | { 40 | std::unique_lock lock(m_mutex); 41 | m_cv.wait(lock, [this]() { return m_count != 0; }); 42 | --m_count; 43 | } 44 | 45 | template 46 | bool wait_for(const std::chrono::duration& t) 47 | { 48 | std::unique_lock lock(m_mutex); 49 | if(!m_cv.wait_for(lock, t, [this]() { return m_count != 0; })) 50 | return false; 51 | --m_count; 52 | return true; 53 | } 54 | 55 | template 56 | bool wait_until(const std::chrono::time_point& t) 57 | { 58 | std::unique_lock lock(m_mutex); 59 | if(!m_cv.wait_until(lock, t, [this]() { return m_count != 0; })) 60 | return false; 61 | --m_count; 62 | return true; 63 | } 64 | 65 | private: 66 | unsigned int m_count; 67 | std::mutex m_mutex; 68 | std::condition_variable m_cv; 69 | }; 70 | 71 | class fast_semaphore 72 | { 73 | public: 74 | explicit fast_semaphore(unsigned int count = 0) 75 | : m_count(count), m_semaphore(0) {} 76 | 77 | void post() 78 | { 79 | int count = m_count.fetch_add(1, std::memory_order_release); 80 | if (count < 0) 81 | m_semaphore.post(); 82 | } 83 | 84 | void wait() 85 | { 86 | int count = m_count.fetch_sub(1, std::memory_order_acquire); 87 | if (count < 1) 88 | m_semaphore.wait(); 89 | } 90 | 91 | private: 92 | std::atomic_int m_count; 93 | semaphore m_semaphore; 94 | }; 95 | -------------------------------------------------------------------------------- /src/fmt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | using namespace fmt; 16 | 17 | int main() 18 | { 19 | // Format into a std::string 20 | auto msg1 = fmt::format("The answer is {}", 42); 21 | auto msg2 = "{0}{1}"_format("The answer is ", 42); 22 | 23 | // Print std::string 24 | fmt::print("{}\n", msg1); 25 | fmt::print("{}\n", msg2); 26 | 27 | // Format into memory buffer and print 28 | fmt::memory_buffer out; 29 | format_to(out, "The answer is {0}", "42"); 30 | auto msg3 = string(out.begin(), out.end()); 31 | fmt::print("{}\n", msg3); 32 | 33 | // Reverse order of parameters, 34 | // print to various outputs 35 | fmt::print("{1} {0}\n", 42, "The answer is"); 36 | fmt::print(cout, "{1} {0}\n", 42, "The answer is"); 37 | fmt::print(stdout, "{1} {0}\n", 42, "The answer is"); 38 | 39 | // Named arguments 40 | fmt::print("{first} {second}\n", fmt::arg("first", "The answer is"), fmt::arg("second", 42)); 41 | fmt::print("{second} {first}\n", "second"_a="The answer is", "first"_a=42); 42 | 43 | // printf style formatting 44 | fmt::printf("The answer is %.2f\n", 42.f); 45 | fmt::fprintf(cout, "The answer is %.2f\n", 42.f); 46 | fmt::fprintf(stdout, "The answer is %.2f\n", 42.f); 47 | 48 | // printf style formatting into a std::string 49 | auto msg4 = fmt::sprintf("The answer is %.2f\n", 42.f); 50 | fmt::printf("%s", msg4); 51 | 52 | // Text color and style manipulation 53 | fmt::print(fmt::emphasis::bold, "The text is bold\n"); 54 | fmt::print(fmt::fg(fmt::color::red) | fmt::bg(fmt::color::green), "The color is red and green\n"); 55 | 56 | // Date and time formatting 57 | std::time_t t = std::time(nullptr); 58 | fmt::print("The date and time is {:%Y-%m-%d %H:%M:%S}\n", *std::localtime(&t)); 59 | 60 | // Alignment 61 | fmt::print("{:-<30}\n", "left aligned"); 62 | fmt::print("{:->30}\n", "right aligned"); 63 | fmt::print("{:-^30}\n", "centered"); 64 | } 65 | -------------------------------------------------------------------------------- /src/base64.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "base64.hpp" 6 | #include "ansi_escape_code.hpp" 7 | 8 | using namespace std; 9 | using namespace ansi_escape_code; 10 | 11 | int main(int argc, char** argv) 12 | { 13 | string input 14 | { 15 | "Man is distinguished, not only by his reason, but by this singular passion from other animals, " 16 | "which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable " 17 | "generation of knowledge, exceeds the short vehemence of any carnal pleasure." 18 | }; 19 | string reference 20 | { 21 | "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" 22 | "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" 23 | "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" 24 | "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" 25 | "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" 26 | }; 27 | 28 | try 29 | { 30 | vector data(begin(input), end(input)); 31 | 32 | cout << bold << bright_red << "Input: " << reset << italic << input << reset << endl << endl; 33 | cout << bold << bright_red << "Reference: " << reset << reference << endl << endl; 34 | 35 | auto encoded = base64::encode(data); 36 | cout << bold << bright_red << "Encoded: " << reset << encoded << endl << endl; 37 | 38 | if(encoded != reference) throw runtime_error("Oh snap! Encoded data does not match reference!"); 39 | else cout << bright_red << "Encoded data matches reference :o)" << reset << endl << endl; 40 | 41 | auto decoded = base64::decode(encoded); 42 | string s2(begin(decoded), end(decoded)); 43 | 44 | cout << bold << bright_red << "Decoded: " << reset << italic << s2 << reset << endl << endl; 45 | 46 | if(data != decoded) throw runtime_error("Oh snap! Input data does not match decoded!"); 47 | else cout << bright_red << "Decoded data matches original :o)" << reset << endl << endl; 48 | } 49 | catch(exception& e) 50 | { 51 | cerr << slow_blink << bright_red << e.what() << reset << endl << endl; 52 | } 53 | 54 | return 1; 55 | } 56 | -------------------------------------------------------------------------------- /src/database.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace soci; 9 | 10 | struct Person 11 | { 12 | int ID; 13 | string FirstName; 14 | string LastName; 15 | int DOB; 16 | string EMail; 17 | }; 18 | 19 | namespace soci 20 | { 21 | template<> 22 | struct type_conversion 23 | { 24 | typedef values base_type; 25 | 26 | static void from_base(const values& v, indicator, Person& p) 27 | { 28 | p.ID = v.get("ID"); 29 | p.FirstName = v.get("FirstName"); 30 | p.LastName = v.get("LastName"); 31 | p.DOB = v.get("DOB"); 32 | p.EMail = v.get("EMail"); 33 | } 34 | 35 | static void to_base(const Person& p, values& v, indicator& ind) 36 | { 37 | v.set("ID", p.ID); 38 | v.set("FirstName", p.FirstName); 39 | v.set("LastName", p.LastName); 40 | v.set("DOB", p.DOB); 41 | v.set("EMail", p.EMail); 42 | ind = i_ok; 43 | } 44 | }; 45 | } 46 | 47 | int main() 48 | { 49 | try 50 | { 51 | session sql(mysql, "host=localhost user=root password=''"); 52 | 53 | sql << "CREATE DATABASE blog"; 54 | sql << "USE blog"; 55 | sql << "CREATE TABLE people (ID INT, FirstName TEXT, LastName TEXT, DOB INT, EMail TEXT)"; 56 | 57 | Person him{1, "Martin", "Vorbrodt", 19800830, "martin@vorbrodt.blog"}; 58 | Person her{2, "Dorota", "Vorbrodt", 19800127, "dorota@vorbrodt.blog"}; 59 | sql << "INSERT INTO people (ID, FirstName, LastName, DOB, EMail) VALUES (:ID, :FirstName, :LastName, :DOB, :EMail)", use(him); 60 | sql << "INSERT INTO people (ID, FirstName, LastName, DOB, EMail) VALUES (:ID, :FirstName, :LastName, :DOB, :EMail)", use(her); 61 | 62 | int count{}; 63 | sql << "SELECT COUNT(*) FROM people", into(count); 64 | cout << "Table 'people' has " << count << " row(s)" << endl; 65 | 66 | Person pOut{}; 67 | sql << "SELECT * FROM people WHERE ID = 1", into(pOut); 68 | cout << pOut.FirstName << ", " << pOut.LastName << ", " << pOut.DOB << ", " << pOut.EMail << endl; 69 | 70 | sql << "DROP TABLE people"; 71 | sql << "DROP DATABASE blog"; 72 | } 73 | catch (exception& e) 74 | { 75 | cerr << e.what() << endl; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/visitor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Circle; 6 | class Square; 7 | 8 | class ShapeVisitor 9 | { 10 | public: 11 | virtual ~ShapeVisitor() = default; 12 | virtual void Visit(Circle*) = 0; 13 | virtual void Visit(Square*) = 0; 14 | }; 15 | 16 | class Shape 17 | { 18 | public: 19 | virtual ~Shape() = default; 20 | virtual float Size() const = 0; 21 | virtual float Area() const = 0; 22 | virtual void Accept(ShapeVisitor* v) = 0; 23 | }; 24 | 25 | class Circle : public Shape 26 | { 27 | public: 28 | explicit Circle(float r) : radius(r) {} 29 | virtual float Size() const override { return radius; } 30 | virtual float Area() const override { return 3.14159f * radius * radius; } 31 | virtual void Accept(ShapeVisitor* v) override { v->Visit(this); } 32 | private: 33 | float radius; 34 | }; 35 | 36 | class Square : public Shape 37 | { 38 | public: 39 | explicit Square(float s) : side(s) {} 40 | virtual float Size() const override { return side; } 41 | virtual float Area() const override { return side * side; } 42 | virtual void Accept(ShapeVisitor* v) override { v->Visit(this); } 43 | private: 44 | float side; 45 | }; 46 | 47 | 48 | 49 | class SerializeShapeVisitor : public ShapeVisitor 50 | { 51 | public: 52 | virtual void Visit(Circle* c) override { cout << "Serializing Circle, Radius = " << c->Size() << ", Area = " << c->Area() << endl; } 53 | virtual void Visit(Square* s) override { cout << "Serializing Suqare, Size = " << s->Size() << ", Area = " << s->Area() << endl; } 54 | }; 55 | 56 | class RenderShapeVisitor : public ShapeVisitor 57 | { 58 | public: 59 | virtual void Visit(Circle* c) override { cout << "Rendering Circle, Radius = " << c->Size() << ", Area = " << c->Area() << endl; } 60 | virtual void Visit(Square* s) override { cout << "Rendering Suqare, Size = " << s->Size() << ", Area = " << s->Area() << endl; } 61 | }; 62 | 63 | 64 | 65 | int main() 66 | { 67 | Shape* c = new Circle(1.5f); 68 | Shape* s = new Square(2.2f); 69 | 70 | ShapeVisitor* sv = new SerializeShapeVisitor; 71 | c->Accept(sv); 72 | s->Accept(sv); 73 | 74 | ShapeVisitor* rv = new RenderShapeVisitor; 75 | c->Accept(rv); 76 | s->Accept(rv); 77 | 78 | // sv->Visit(c); 79 | // rv->Visit(s); 80 | } 81 | -------------------------------------------------------------------------------- /src/handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | class basic_handle : public AP, public RP 8 | { 9 | public: 10 | basic_handle(const T& v) : m_handle(v) { AP::Execute(m_handle); } 11 | 12 | template>* = nullptr> 13 | basic_handle(U&& v) : m_handle(std::move(v)) { AP::Execute(m_handle); v = U{}; } 14 | 15 | basic_handle(const basic_handle&) = delete; 16 | 17 | template>* = nullptr> 18 | basic_handle(basic_handle&& other) : m_handle(std::move(other.m_handle)) { other.m_handle = U{}; } 19 | 20 | ~basic_handle() { RP::Execute(m_handle); } 21 | 22 | basic_handle& operator = (const T& v) = delete; 23 | 24 | template 25 | std::enable_if_t, basic_handle&> 26 | operator = (U&& v) 27 | { 28 | RP::Execute(m_handle); 29 | m_handle = std::move(v); 30 | AP::Execute(m_handle); 31 | v = U{}; 32 | return *this; 33 | } 34 | 35 | basic_handle& operator = (const basic_handle&) = delete; 36 | 37 | template 38 | std::enable_if_t, basic_handle&> 39 | operator = (basic_handle&& other) 40 | { 41 | RP::Execute(m_handle); 42 | m_handle = std::move(other.m_handle); 43 | other.m_handle = U{}; 44 | return *this; 45 | } 46 | 47 | operator T& () { return m_handle; } 48 | operator const T& () const { return m_handle; } 49 | 50 | template 51 | std::enable_if_t, U*> 52 | operator & () { return &m_handle; } 53 | 54 | private: 55 | T m_handle; 56 | }; 57 | 58 | struct NoOpPolicy { template void Execute(const T&) {} }; 59 | 60 | struct PointerReleasePolicy { template void Execute(T* ptr) { delete ptr; } }; 61 | template using ptr_handle_t = basic_handle; 62 | 63 | struct PointerArrayReleasePolicy { template void Execute(T* ptr) { delete[] ptr; } }; 64 | template using arr_ptr_handle_t = basic_handle; 65 | -------------------------------------------------------------------------------- /src/istring.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "istring.hpp" 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | auto f_std = [](const string& s) {}; 9 | auto f_is = [](const istring& s) {}; 10 | 11 | auto std1 = string{ "abc" }; 12 | auto std2 = "string literal"s; 13 | auto std3 = string{ "istring literal"_is }; 14 | 15 | auto istr1 = istring{ "ABC" }; 16 | auto istr2 = "istring literal"_is; 17 | auto istr3 = istring{ "string literal"s }; 18 | 19 | auto std4 = string{ std::move(istr3) }; 20 | auto istr4 = istring{ std::move(std3) }; 21 | 22 | f_std((string)istr1); 23 | f_is((istring)std1); 24 | 25 | std1 = (string)istr1; 26 | istr1 = (istring)std1; 27 | 28 | auto result = false; 29 | 30 | result = (std1 == istr1); 31 | result = (std1 != istr1); 32 | result = (std1 < istr1); 33 | result = (std1 <= istr1); 34 | result = (std1 > istr1); 35 | result = (std1 >= istr1); 36 | 37 | result = (istr1 == std1); 38 | result = (istr1 != std1); 39 | result = (istr1 < std1); 40 | result = (istr1 <= std1); 41 | result = (istr1 > std1); 42 | result = (istr1 >= std1); 43 | 44 | auto is1 = istring{ "aaa" }; 45 | auto is2 = istring{ "AAA" }; 46 | cout << "'" << is1 << "' == '" << is2 << "' 2-way: " << boolalpha 47 | << (is1 == is2) << ", 3-way: " << is1.compare(is2) << endl; 48 | 49 | is1 = "aaa"_is; 50 | is2 = "bbb"_is; 51 | cout << "'" << is1 << "' == '" << is2 << "' 2-way: " << boolalpha 52 | << (is1 == is2) << ", 3-way: " << is1.compare(is2) << endl; 53 | 54 | is1 = "bbb"_is; 55 | is2 = "aaa"_is; 56 | cout << "'" << is1 << "' == '" << is2 << "' 2-way: " << boolalpha 57 | << (is1 == is2) << ", 3-way: " << is1.compare(is2) << endl; 58 | 59 | auto iws1 = iwstring{ L"aaa" }; 60 | auto iws2 = iwstring{ L"AAA" }; 61 | wcout << L"'" << iws1 << L"' == '" << iws2 << L"' 2-way: " << boolalpha 62 | << (iws1 == iws2) << L", 3-way: " << iws1.compare(iws2) << endl; 63 | 64 | iws1 = L"aaa"_is; 65 | iws2 = L"bbb"_is; 66 | wcout << L"'" << iws1 << L"' == '" << iws2 << L"' 2-way: " << boolalpha 67 | << (iws1 == iws2) << L", 3-way: " << iws1.compare(iws2) << endl; 68 | 69 | iws1 = L"bbb"_is; 70 | iws2 = L"aaa"_is; 71 | wcout << L"'" << iws1 << L"' == '" << iws2 << L"' 2-way: " << boolalpha 72 | << (iws1 == iws2) << L", 3-way: " << iws1.compare(iws2) << endl; 73 | } 74 | -------------------------------------------------------------------------------- /cmake/FindWolfSSL.cmake: -------------------------------------------------------------------------------- 1 | if(WOLFSSL_PREFER_STATIC_LIB) 2 | set(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) 3 | if(WIN32) 4 | set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib ${CMAKE_FIND_LIBRARY_SUFFIXES}) 5 | else() 6 | set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 7 | endif() 8 | endif() 9 | 10 | if(UNIX) 11 | find_package(PkgConfig QUIET) 12 | pkg_check_modules(_WOLFSSL QUIET wolfssl) 13 | endif() 14 | 15 | find_path(WOLFSSL_INCLUDE_DIR NAMES wolfssl/version.h HINTS ${_WOLFSSL_INCLUDEDIR}) 16 | find_library(WOLFSSL_LIBRARY NAMES wolfssl HINTS ${_WOLFSSL_LIBDIR}) 17 | if(WOLFSSL_INCLUDE_DIR AND WOLFSSL_LIBRARY) 18 | set(WOLFSSL_INCLUDE_DIR ${WOLFSSL_INCLUDE_DIR}) 19 | set(WOLFSSL_LIBRARY ${WOLFSSL_LIBRARY}) 20 | set(WOLFSSL_VERSION ${_WOLFSSL_VERSION}) 21 | set(WOLFSSL_IS_WOLFSSL ON) 22 | else() 23 | if(UNIX) 24 | pkg_check_modules(_WOLFSSL QUIET WOLFSSL) 25 | endif() 26 | 27 | find_path(WOLFSSL_INCLUDE_DIR NAMES WOLFSSL/version.h HINTS ${_WOLFSSL_INCLUDEDIR}) 28 | find_library(WOLFSSL_LIBRARY NAMES WOLFSSL HINTS ${_WOLFSSL_LIBDIR}) 29 | set(WOLFSSL_VERSION ${_WOLFSSL_VERSION}) 30 | set(WOLFSSL_IS_WOLFSSL OFF) 31 | endif() 32 | 33 | if(NOT WOLFSSL_VERSION AND WOLFSSL_INCLUDE_DIR) 34 | if(WOLFSSL_IS_WOLFSSL) 35 | file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"") 36 | else() 37 | file(STRINGS "${WOLFSSL_INCLUDE_DIR}/WOLFSSL/version.h" WOLFSSL_VERSION_STR REGEX "^#define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"[^\"]+\"") 38 | endif() 39 | if(WOLFSSL_VERSION_STR MATCHES "\"([^\"]+)\"") 40 | set(WOLFSSL_VERSION "${CMAKE_MATCH_1}") 41 | endif() 42 | endif() 43 | 44 | set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) 45 | set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) 46 | 47 | include(FindPackageHandleStandardArgs) 48 | 49 | find_package_handle_standard_args(WOLFSSL 50 | REQUIRED_VARS 51 | WOLFSSL_LIBRARY 52 | WOLFSSL_INCLUDE_DIR 53 | VERSION_VAR 54 | WOLFSSL_VERSION 55 | ) 56 | 57 | mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) 58 | 59 | if(WOLFSSL_PREFER_STATIC_LIB) 60 | set(CMAKE_FIND_LIBRARY_SUFFIXES ${WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) 61 | unset(WOLFSSL_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) 62 | endif() 63 | -------------------------------------------------------------------------------- /src/lesson_pool_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace std::chrono; 9 | 10 | struct timer 11 | { 12 | ~timer() { 13 | auto e = high_resolution_clock::now() - m_t; 14 | cout << duration_cast(e).count() << " ms" << endl; } 15 | 16 | high_resolution_clock::time_point m_t = high_resolution_clock::now(); 17 | }; 18 | 19 | using payload_t = unsigned long long; 20 | auto payload = payload_t{}; 21 | 22 | struct P 23 | { 24 | P() : m_v{ ++payload } {} 25 | payload_t m_v; 26 | 27 | using pool = boost::singleton_pool; 28 | 29 | void* operator new (std::size_t n) { return pool::malloc(); } 30 | void* operator new [] (std::size_t n) { return pool::ordered_malloc(n / sizeof(payload_t)); } 31 | 32 | void operator delete (void* p) { pool::free(p); } 33 | void operator delete [] (void* p) { pool::free(p); } 34 | }; 35 | 36 | static_assert(sizeof(P) == sizeof(payload_t)); 37 | 38 | struct NP 39 | { 40 | NP() : m_v{ ++payload } {} 41 | payload_t m_v; 42 | }; 43 | 44 | static_assert(sizeof(NP) == sizeof(payload_t)); 45 | 46 | template 47 | void run_impl(int c, int n) 48 | { 49 | vector v; 50 | v.reserve(n); 51 | for(auto i = 0; i < c; ++i) 52 | { 53 | for(auto it = 0; it < n; ++it) 54 | v.push_back(new T); 55 | for(auto p : v) 56 | delete p; 57 | v.clear(); 58 | } 59 | } 60 | 61 | template 62 | void run(int c, int n) 63 | { 64 | timer t; 65 | run_impl(c, n); 66 | } 67 | 68 | template 69 | void runN(int c, int n, int ts) 70 | { 71 | timer t; 72 | vector v; 73 | for(auto t = 0; t < ts; ++t) 74 | v.emplace_back([=]() { run_impl(c, n); }); 75 | for(auto& t : v) 76 | t.join();} 77 | 78 | int main() 79 | { 80 | timer runtime; 81 | 82 | auto C = 100'000; 83 | auto N = 100; 84 | auto T = thread::hardware_concurrency(); 85 | 86 | cout << "run (" << C << ", " << N << ") w/ pool: "; 87 | run

(C, N); 88 | 89 | cout << "run (" << C << ", " << N << ") w/o pool: "; 90 | run(C, N); 91 | 92 | cout << endl; 93 | 94 | cout << "runN (" << C << ", " << N << ") w/ pool: "; 95 | runN

(C, N, T); 96 | 97 | cout << "runN (" << C << ", " << N << ") w/o pool: "; 98 | runN(C, N, T); 99 | 100 | cout << endl << "runtime: "; 101 | } 102 | -------------------------------------------------------------------------------- /CMakeLists.txt.msvc: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | 3 | set(CMAKE_TOOLCHAIN_FILE "C:/Code/vcpkg/scripts/buildsystems/vcpkg.cmake") 4 | 5 | project(blog VERSION 1.0 6 | DESCRIPTION "Vorbrodt's C++ Blog - Code Samples" 7 | HOMEPAGE_URL "https://vorbrodt.blog" 8 | LANGUAGES C CXX) 9 | 10 | set(CMAKE_CXX_STANDARD 20) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | set(CMAKE_CXX_EXTENSIONS OFF) 13 | 14 | if(NOT CMAKE_BUILD_TYPE) 15 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Default build type: Release" FORCE) 16 | endif() 17 | 18 | find_package(Threads MODULE REQUIRED) 19 | find_package(TBB CONFIG REQUIRED) 20 | find_package(ParallelSTL CONFIG REQUIRED) 21 | find_package(Boost COMPONENTS serialization program_options timer stacktrace_windbg filesystem system REQUIRED) 22 | find_package(Range-v3 CONFIG REQUIRED) 23 | find_package(benchmark CONFIG REQUIRED) 24 | find_package(Catch2 CONFIG REQUIRED) 25 | # find_package(Hayai CONFIG REQUIRED) 26 | find_package(nonius CONFIG REQUIRED) 27 | find_package(celero CONFIG REQUIRED) 28 | find_package(FMT CONFIG REQUIRED) 29 | find_package(cryptopp CONFIG REQUIRED) 30 | find_package(libmysql REQUIRED) 31 | find_package(Thrift CONFIG REQUIRED) 32 | find_package(Protobuf MODULE REQUIRED) 33 | find_package(gRPC CONFIG REQUIRED) 34 | find_package(lz4 CONFIG REQUIRED) 35 | find_package(OpenSSL MODULE REQUIRED) 36 | find_package(CURL MODULE REQUIRED) 37 | find_package(unofficial-curlpp CONFIG REQUIRED) 38 | find_package(WolfSSL CONFIG REQUIRED) 39 | # find_package(BOTAN CONFIG REQUIRED) 40 | # find_package(MYSQL REQUIRED) # no cmake 41 | # find_package(SOCI CONFIG REQUIRED) 42 | # find_package(XMLRPC REQUIRED) 43 | 44 | include_directories("C:\\Code\\vcpkg\\installed\\x64-windows\\include") 45 | link_directories("C:\\Code\\vcpkg\\installed\\x64-windows\\lib") 46 | 47 | set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") 48 | 49 | set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 50 | set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 51 | 52 | add_subdirectory(src) 53 | 54 | # get_cmake_property(_variableNames VARIABLES) 55 | # list (SORT _variableNames) 56 | # foreach (_variableName ${_variableNames}) 57 | # message(STATUS "${_variableName}=${${_variableName}}") 58 | # endforeach() 59 | -------------------------------------------------------------------------------- /src/event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | class manual_event 10 | { 11 | public: 12 | explicit manual_event(bool signaled = false) 13 | : m_signaled(signaled) {} 14 | 15 | void signal() 16 | { 17 | { 18 | std::unique_lock lock(m_mutex); 19 | m_signaled = true; 20 | } 21 | m_cv.notify_all(); 22 | } 23 | 24 | void wait() 25 | { 26 | std::unique_lock lock(m_mutex); 27 | m_cv.wait(lock, [&]() { return m_signaled != false; }); 28 | } 29 | 30 | template 31 | bool wait_for(const std::chrono::duration& t) 32 | { 33 | std::unique_lock lock(m_mutex); 34 | bool result = m_cv.wait_for(lock, t, [&]() { return m_signaled != false; }); 35 | return result; 36 | } 37 | 38 | template 39 | bool wait_until(const std::chrono::time_point& t) 40 | { 41 | std::unique_lock lock(m_mutex); 42 | bool result = m_cv.wait_until(lock, t, [&]() { return m_signaled != false; }); 43 | return result; 44 | } 45 | 46 | void reset() 47 | { 48 | std::unique_lock lock(m_mutex); 49 | m_signaled = false; 50 | } 51 | 52 | private: 53 | bool m_signaled = false; 54 | std::mutex m_mutex; 55 | std::condition_variable m_cv; 56 | }; 57 | 58 | 59 | 60 | class auto_event 61 | { 62 | public: 63 | explicit auto_event(bool signaled = false) 64 | : m_signaled(signaled) {} 65 | 66 | void signal() 67 | { 68 | { 69 | std::unique_lock lock(m_mutex); 70 | m_signaled = true; 71 | } 72 | m_cv.notify_one(); 73 | } 74 | 75 | void wait() 76 | { 77 | std::unique_lock lock(m_mutex); 78 | m_cv.wait(lock, [&]() { return m_signaled != false; }); 79 | m_signaled = false; 80 | } 81 | 82 | template 83 | bool wait_for(const std::chrono::duration& t) 84 | { 85 | std::unique_lock lock(m_mutex); 86 | bool result = m_cv.wait_for(lock, t, [&]() { return m_signaled != false; }); 87 | if(result) m_signaled = false; 88 | return result; 89 | } 90 | 91 | template 92 | bool wait_until(const std::chrono::time_point& t) 93 | { 94 | std::unique_lock lock(m_mutex); 95 | bool result = m_cv.wait_until(lock, t, [&]() { return m_signaled != false; }); 96 | if(result) m_signaled = false; 97 | return result; 98 | } 99 | 100 | private: 101 | bool m_signaled = false; 102 | std::mutex m_mutex; 103 | std::condition_variable m_cv; 104 | }; 105 | -------------------------------------------------------------------------------- /src/singleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "singleton.hpp" 4 | 5 | using namespace std; 6 | 7 | SINGLETON_CLASS(A) {}; 8 | SINGLETON_STRUCT(B) {}; 9 | struct C final : public singleton {}; 10 | 11 | ABSTRACT_SINGLETON_CLASS(AA) {}; 12 | ABSTRACT_SINGLETON_STRUCT(AB) {}; 13 | struct AC : public abstract_singleton {}; 14 | 15 | class S SINGLETON(S) 16 | { 17 | public: 18 | ~S() { cout << "~S()" << endl; } 19 | void foo() { cout << "S::foo() x = " << _x << endl; } 20 | void bar() const { cout << "S::bar() x = " << _x << endl; } 21 | 22 | private: 23 | // Constructor must be private to prevent creation of instances... 24 | // ...except by singleton base class, which is our friend... 25 | SINGLETON_FRIEND(S); 26 | S(int x = -17) : _x(x) { cout << "S(" << _x << ")" << endl; } 27 | int _x = 0; 28 | }; 29 | 30 | class AS ABSTRACT_SINGLETON(AS) 31 | { 32 | public: 33 | // No friendship needed if constructors are public... 34 | // ...but you still can't create instances of AS... 35 | // ...except by abstract_singleton base class... 36 | // ...which internally erases the abstraction... 37 | //ABSTRACT_SINGLETON_FRIEND(AS); 38 | AS(int x = -20) : _x(x) { cout << "AS(" << _x << ")" << endl; } 39 | ~AS() { cout << "~AS()" << endl; } 40 | void foo() { cout << "AS::foo() x = " << _x << endl; } 41 | void bar() const { cout << "AS::bar() x = " << _x << endl; } 42 | 43 | private: 44 | int _x = 0; 45 | }; 46 | 47 | int main() 48 | { 49 | try { if(S::Instance() == nullptr) throw logic_error("class S not created yet!"); } 50 | catch (exception& e) { cerr << e.what() << endl; } 51 | 52 | S::Create(17); 53 | //S s(17); // Compile-time error, can't create instances... 54 | 55 | S::Create(-17); // no-op, already created... 56 | 57 | //*S::Instance() = *S::Instance(); // Compile-time error, can't copy/move singletons... 58 | S::Instance()->foo(); 59 | S::Instance()->bar(); 60 | S::Destroy(); 61 | S::Destroy(); // no-op, already destroyed... 62 | 63 | try { if(AS::Instance() == nullptr) throw logic_error("class AS not created yet!"); } 64 | catch (exception& e) { cerr << e.what() << endl; } 65 | 66 | AS::Create(20); 67 | //AS s(20); // Compile-time error, can't create instances... 68 | 69 | AS::Create(-20); // no-op, already created... 70 | 71 | //*AS::Instance() = *AS::Instance(); // Compile-time error, can't copy/move singletons... 72 | AS::Instance()->foo(); 73 | AS::Instance()->bar(); 74 | AS::Destroy(); 75 | AS::Destroy(); // no-op, already destroyed... 76 | 77 | cout << "Done!" << endl; 78 | } 79 | -------------------------------------------------------------------------------- /cmake/FindGRPC.cmake: -------------------------------------------------------------------------------- 1 | find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin) # Get full path to plugin 2 | 3 | find_library(GRPC_LIBRARY NAMES grpc) 4 | find_library(GRPCPP_LIBRARY NAMES grpc++) 5 | find_library(GPR_LIBRARY NAMES gpr) 6 | set(GRPC_LIBRARIES ${GRPCPP_LIBRARY} ${GRPC_LIBRARY} ${GPR_LIBRARY}) 7 | if(GRPC_LIBRARIES) 8 | message(STATUS "Found GRPC: ${GRPC_LIBRARIES}; plugin - ${GRPC_CPP_PLUGIN}") 9 | endif() 10 | 11 | 12 | function(PROTOBUF_GENERATE_GRPC_CPP SRCS HDRS) 13 | if(NOT ARGN) 14 | message(SEND_ERROR "Error: PROTOBUF_GENERATE_GRPC_CPP() called without any proto files") 15 | return() 16 | endif() 17 | 18 | if(PROTOBUF_GENERATE_CPP_APPEND_PATH) # This variable is common for all types of output. 19 | # Create an include path for each file specified 20 | foreach(FIL ${ARGN}) 21 | get_filename_component(ABS_FIL ${FIL} ABSOLUTE) 22 | get_filename_component(ABS_PATH ${ABS_FIL} PATH) 23 | list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) 24 | if(${_contains_already} EQUAL -1) 25 | list(APPEND _protobuf_include_path -I ${ABS_PATH}) 26 | endif() 27 | endforeach() 28 | else() 29 | set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) 30 | endif() 31 | 32 | if(DEFINED PROTOBUF_IMPORT_DIRS) 33 | foreach(DIR ${PROTOBUF_IMPORT_DIRS}) 34 | get_filename_component(ABS_PATH ${DIR} ABSOLUTE) 35 | list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) 36 | if(${_contains_already} EQUAL -1) 37 | list(APPEND _protobuf_include_path -I ${ABS_PATH}) 38 | endif() 39 | endforeach() 40 | endif() 41 | 42 | set(${SRCS}) 43 | set(${HDRS}) 44 | foreach(FIL ${ARGN}) 45 | get_filename_component(ABS_FIL ${FIL} ABSOLUTE) 46 | get_filename_component(FIL_WE ${FIL} NAME_WE) 47 | 48 | list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc") 49 | list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h") 50 | 51 | add_custom_command( 52 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.cc" 53 | "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.grpc.pb.h" 54 | COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} 55 | ARGS --grpc_out=${CMAKE_CURRENT_BINARY_DIR} 56 | --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} 57 | ${_protobuf_include_path} ${ABS_FIL} 58 | DEPENDS ${ABS_FIL} ${PROTOBUF_PROTOC_EXECUTABLE} 59 | COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}" 60 | VERBATIM) 61 | endforeach() 62 | 63 | set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) 64 | set(${SRCS} ${${SRCS}} PARENT_SCOPE) 65 | set(${HDRS} ${${HDRS}} PARENT_SCOPE) 66 | endfunction() 67 | -------------------------------------------------------------------------------- /src/newtrace.st.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED 11 | #include 12 | 13 | namespace 14 | { 15 | template 16 | struct malloc_allocator_t : std::allocator 17 | { 18 | T* allocate(std::size_t n) 19 | { 20 | T* ptr = (T*)std::malloc(n * sizeof(T)); 21 | if(!ptr) throw std::bad_alloc(); 22 | return ptr; 23 | } 24 | 25 | void deallocate(T* ptr, std::size_t) { std::free(ptr); } 26 | 27 | template 28 | struct rebind { typedef malloc_allocator_t other; }; 29 | }; 30 | 31 | using char_t = char; 32 | using string_t = const char_t*; 33 | using stack_trace_t = std::string; 34 | using new_entry_t = struct { std::size_t bytes; string_t file; string_t proc; int line; stack_trace_t stack; }; 35 | using ptr_t = void*; 36 | using new_ptr_map_t = std::map, malloc_allocator_t>>; 37 | using leak_list_t = std::vector; 38 | 39 | inline static auto& get_ptr_map() 40 | { 41 | static new_ptr_map_t new_ptr_map; 42 | return new_ptr_map; 43 | } 44 | 45 | inline static auto get_leaks() 46 | { 47 | leak_list_t leaks; 48 | leaks.reserve(get_ptr_map().size()); 49 | for(const auto& it : get_ptr_map()) 50 | leaks.push_back(it.second); 51 | return leaks; 52 | } 53 | } 54 | 55 | void* operator new(std::size_t n) 56 | { 57 | void* ptr = std::malloc(n); 58 | if(!ptr) throw std::bad_alloc(); 59 | return ptr; 60 | } 61 | 62 | void* operator new (std::size_t n, const char* file, const char* func, int line) 63 | { 64 | void* ptr = ::operator new(n); 65 | try { 66 | namespace st = boost::stacktrace; 67 | auto stack = st::to_string(st::stacktrace()); 68 | static std::recursive_mutex operator_new_lock; 69 | std::scoped_lock guard{ operator_new_lock }; 70 | get_ptr_map().emplace(ptr, new_entry_t{ n, file, func, line, std::move(stack) }); 71 | } catch(...) { } 72 | return ptr; 73 | } 74 | 75 | void* operator new [] (std::size_t n, const char* file, const char* func, int line) 76 | { 77 | return ::operator new(n, file, func, line); 78 | } 79 | 80 | void operator delete (void* ptr) noexcept 81 | { 82 | static std::recursive_mutex operator_delete_lock; 83 | std::scoped_lock guard{ operator_delete_lock }; 84 | auto it = get_ptr_map().find(ptr); 85 | if(it != get_ptr_map().end()) 86 | get_ptr_map().erase(it); 87 | std::free(ptr); 88 | } 89 | 90 | #if defined(new) 91 | #error Macro 'new' is already defined! 92 | #else 93 | #define new new(__FILE_NAME__, __FUNCTION__, __LINE__) 94 | #endif 95 | -------------------------------------------------------------------------------- /src/lesson_pool_allocator_benchmark.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | using seq_t = uint64_t; 17 | using payload_t = struct { std::byte data[16]; }; 18 | 19 | static_assert(sizeof(payload_t) >= sizeof(seq_t)); 20 | 21 | auto gen_next_payload() 22 | { 23 | static auto seq = seq_t{ 1 }; 24 | 25 | payload_t p; 26 | memcpy(&p, &seq, sizeof(seq)); 27 | ++seq; 28 | 29 | return p; 30 | } 31 | 32 | struct P 33 | { 34 | P() : m_v{ gen_next_payload() } {} 35 | payload_t m_v; 36 | 37 | using pool = boost::singleton_pool; 38 | 39 | void* operator new (std::size_t n) { return pool::malloc(); } 40 | void* operator new [] (std::size_t n) = delete; 41 | 42 | void operator delete (void* p) { pool::free(p); } 43 | void operator delete [] (void* p) = delete; 44 | }; 45 | 46 | static_assert(sizeof(P) == sizeof(payload_t)); 47 | 48 | struct NP 49 | { 50 | NP() : m_v{ gen_next_payload() } {} 51 | payload_t m_v; 52 | }; 53 | 54 | static_assert(sizeof(NP) == sizeof(payload_t)); 55 | 56 | template 57 | void run_impl(int c, int n) 58 | { 59 | static mt19937 gen((random_device{})()); 60 | 61 | vector v; 62 | v.reserve(n); 63 | for(auto i = 0; i < c; ++i) 64 | { 65 | for(auto it = 0; it < n; ++it) 66 | v.push_back(new T); 67 | 68 | // simulate randomness in allocations/deallocations 69 | shuffle(begin(v), end(v), gen); 70 | 71 | for(auto p : v) 72 | delete p; 73 | v.clear(); 74 | } 75 | } 76 | 77 | template 78 | void run(int c, int n) 79 | { 80 | run_impl(c, n); 81 | } 82 | 83 | template 84 | void runN(int c, int n, int ts) 85 | { 86 | vector v; 87 | v.reserve(ts); 88 | for(auto t = 0; t < ts; ++t) 89 | v.emplace_back([=]() { run_impl(c, n); }); 90 | for(auto& t : v) 91 | t.join(); 92 | } 93 | 94 | TEST_CASE("Pool Allocator. Chunk size = "s + to_string(sizeof(payload_t)), "[benchmark]") 95 | { 96 | auto C = 10; 97 | auto N = 100'000; 98 | auto T = thread::hardware_concurrency(); 99 | 100 | BENCHMARK("1 thread w/ pool") 101 | { 102 | run

(C, N); 103 | }; 104 | BENCHMARK("1 thread w/o pool") 105 | { 106 | run(C, N); 107 | }; 108 | BENCHMARK(to_string(T) + " threads, w/ pool") 109 | { 110 | runN

(C, N, T); 111 | }; 112 | BENCHMARK(to_string(T) + " threads, w/o pool") 113 | { 114 | runN(C, N, T); 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /src/lesson_thread_pool_how_to.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class thread_pool 15 | { 16 | public: 17 | explicit thread_pool(std::size_t thread_count = std::thread::hardware_concurrency()) 18 | { 19 | if(!thread_count) 20 | throw std::invalid_argument("bad thread count! must be non-zero!"); 21 | 22 | m_threads.reserve(thread_count); 23 | 24 | for(auto i = 0; i < thread_count; ++i) 25 | { 26 | m_threads.push_back(std::thread([this]() 27 | { 28 | while(true) 29 | { 30 | work_item_ptr_t work{nullptr}; 31 | { 32 | std::unique_lock guard(m_queue_lock); 33 | m_condition.wait(guard, [&]() { return !m_queue.empty(); }); 34 | work = std::move(m_queue.front()); 35 | m_queue.pop(); 36 | } 37 | if(!work) 38 | { 39 | break; 40 | } 41 | (*work)(); 42 | } 43 | })); 44 | } 45 | } 46 | 47 | ~thread_pool() 48 | { 49 | { 50 | std::unique_lock guard(m_queue_lock); 51 | for(auto& t : m_threads) 52 | m_queue.push(work_item_ptr_t{nullptr}); 53 | } 54 | for(auto& t : m_threads) 55 | t.join(); 56 | } 57 | 58 | thread_pool(const thread_pool&) = delete; 59 | thread_pool(thread_pool&&) = delete; 60 | thread_pool& operator = (const thread_pool&) = delete; 61 | thread_pool& operator = (thread_pool&&) = delete; 62 | 63 | using work_item_t = std::function; 64 | 65 | void do_work(work_item_t wi) 66 | { 67 | auto work_item = std::make_unique(std::move(wi)); 68 | { 69 | std::unique_lock guard(m_queue_lock); 70 | m_queue.push(std::move(work_item)); 71 | } 72 | m_condition.notify_one(); 73 | } 74 | 75 | private: 76 | using work_item_ptr_t = std::unique_ptr; 77 | using work_queue_t = std::queue; 78 | 79 | work_queue_t m_queue; 80 | std::mutex m_queue_lock; 81 | std::condition_variable m_condition; 82 | 83 | using threads_t = std::vector; 84 | threads_t m_threads; 85 | }; 86 | 87 | int main() 88 | { 89 | using namespace std; 90 | srand(time(NULL)); 91 | mutex cout_guard; 92 | 93 | cout << "main thread ID: " << this_thread::get_id() << endl; 94 | 95 | thread_pool tp; 96 | 97 | for(auto i = 1; i <= 50; i++) 98 | tp.do_work([&, i = i]() 99 | { 100 | { 101 | unique_lock guard(cout_guard); 102 | cout << "doing work " << i << "..." << endl; 103 | } 104 | this_thread::sleep_for(chrono::milliseconds(rand() % 1000)); 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /src/singleton.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | class singleton 10 | { 11 | public: 12 | template 13 | static void Create(Args&&... args) 14 | { 15 | static std::once_flag s_create_once; 16 | std::call_once(s_create_once, 17 | [...args = std::forward(args)] () mutable 18 | { s_instance.reset(new T(std::forward(args)...)); }); 19 | } 20 | 21 | static void Destroy() 22 | { 23 | static std::once_flag s_destroy_once; 24 | std::call_once(s_destroy_once, [] { s_instance.reset(); }); 25 | } 26 | 27 | static T* Instance() noexcept { return s_instance.get(); } 28 | 29 | protected: 30 | singleton() = default; 31 | singleton(const singleton&) = delete; 32 | singleton(singleton&&) = delete; 33 | singleton& operator = (const singleton&) = delete; 34 | singleton& operator = (singleton&&) = delete; 35 | ~singleton() = default; 36 | 37 | private: 38 | using storage_t = std::unique_ptr; 39 | inline static storage_t s_instance = nullptr; 40 | }; 41 | 42 | #define SINGLETON(T) final : public singleton 43 | #define SINGLETON_CLASS(C) class C SINGLETON(C) 44 | #define SINGLETON_STRUCT(S) struct S SINGLETON(S) 45 | #define SINGLETON_FRIEND(T) friend class singleton 46 | 47 | template 48 | class abstract_singleton 49 | { 50 | public: 51 | template 52 | static void Create(Args&&... args) 53 | { 54 | struct Q : T 55 | { 56 | using T::T; 57 | virtual void pure_virtual() const final override {} 58 | }; 59 | 60 | static std::once_flag s_create_once; 61 | std::call_once(s_create_once, 62 | [...args = std::forward(args)] () mutable 63 | { s_instance.reset(new Q(std::forward(args)...)); }); 64 | } 65 | 66 | static void Destroy() 67 | { 68 | static std::once_flag s_destroy_once; 69 | std::call_once(s_destroy_once, [] { s_instance.reset(); }); 70 | } 71 | 72 | static T* Instance() noexcept { return s_instance.get(); } 73 | 74 | protected: 75 | abstract_singleton() = default; 76 | abstract_singleton(const abstract_singleton&) = delete; 77 | abstract_singleton(abstract_singleton&&) = delete; 78 | abstract_singleton& operator = (const abstract_singleton&) = delete; 79 | abstract_singleton& operator = (abstract_singleton&&) = delete; 80 | virtual ~abstract_singleton() = default; 81 | 82 | private: 83 | using storage_t = std::unique_ptr; 84 | inline static storage_t s_instance = nullptr; 85 | 86 | virtual void pure_virtual() const = 0; 87 | }; 88 | 89 | #define ABSTRACT_SINGLETON(T) : public abstract_singleton 90 | #define ABSTRACT_SINGLETON_CLASS(C) class C ABSTRACT_SINGLETON(C) 91 | #define ABSTRACT_SINGLETON_STRUCT(S) struct S ABSTRACT_SINGLETON(S) 92 | #define ABSTRACT_SINGLETON_FRIEND(T) friend class abstract_singleton 93 | -------------------------------------------------------------------------------- /src/aos_soa.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | using namespace chrono; 10 | 11 | const int ELEMS = 10'000'000; 12 | 13 | struct Person 14 | { 15 | Person(const string& n, uint8_t a, uint32_t d) 16 | : name(n), age(a), dob(d) {} 17 | 18 | string name; 19 | uint8_t age; 20 | uint32_t dob; 21 | }; 22 | 23 | using VP = vector; 24 | 25 | void addPerson(VP& v, Person&& p) { v.push_back(move(p)); } 26 | 27 | uint64_t averageNameLen(const VP& v) 28 | { 29 | return accumulate(begin(v), end(v), (uint64_t)0, 30 | [](auto sum, auto& p) { return sum + p.name.length(); }) / v.size(); 31 | } 32 | 33 | uint64_t averageAge(const VP& v) 34 | { 35 | return accumulate(begin(v), end(v), (uint64_t)0, 36 | [](auto sum, auto& p) { return sum + p.age; }) / v.size(); 37 | } 38 | 39 | uint64_t averageDob(const VP& v) 40 | { 41 | return accumulate(begin(v), end(v), (uint64_t)0, 42 | [](auto sum, auto& p) { return sum + p.dob; }) / v.size(); 43 | } 44 | 45 | struct Persons 46 | { 47 | vector names; 48 | vector ages; 49 | vector dobs; 50 | 51 | void addPerson(const string& n, uint8_t a, uint32_t d) 52 | { 53 | names.push_back(n); 54 | ages.push_back(a); 55 | dobs.push_back(d); 56 | } 57 | 58 | uint64_t averageNameLen() const 59 | { 60 | return accumulate(begin(names), end(names), (uint64_t)0, 61 | [](auto sum, auto& n) { return sum + n.length(); }) / names.size(); 62 | } 63 | 64 | uint64_t averageAge() const 65 | { 66 | return accumulate(begin(ages), end(ages), (uint64_t)0) / ages.size(); 67 | } 68 | 69 | uint64_t averageDob() const 70 | { 71 | return accumulate(begin(dobs), end(dobs), (uint64_t)0) / dobs.size(); 72 | } 73 | }; 74 | 75 | int main() 76 | { 77 | VP v1; 78 | v1.reserve(ELEMS); 79 | for(int i = 0; i < ELEMS; ++i) 80 | addPerson(v1, Person(string(string().capacity(), 'N'), i % 0xFF, i % 0xFFFF)); 81 | 82 | auto start_time = high_resolution_clock::now(); 83 | auto sum = averageNameLen(v1); 84 | sum += averageAge(v1); 85 | sum += averageDob(v1); 86 | auto end_time = high_resolution_clock::now(); 87 | cout << fixed << setprecision(3); 88 | cout << "AoS duration " << duration_cast(end_time - start_time).count() / 1000.f << " ms" << endl; 89 | v1.clear(); 90 | v1.shrink_to_fit(); 91 | 92 | Persons p; 93 | p.names.reserve(ELEMS); 94 | p.ages.reserve(ELEMS); 95 | p.dobs.reserve(ELEMS); 96 | for(int i = 0; i < ELEMS; ++i) 97 | p.addPerson(string(string().capacity(), 'N'), rand() % 0xFF, rand() % 0xFFFF); 98 | 99 | start_time = high_resolution_clock::now(); 100 | sum += p.averageNameLen(); 101 | sum += p.averageAge(); 102 | sum += p.averageDob(); 103 | end_time = high_resolution_clock::now(); 104 | cout << "SoA duration " << duration_cast(end_time - start_time).count() / 1000.f << " ms" << endl; 105 | 106 | return sum; 107 | } 108 | -------------------------------------------------------------------------------- /src/unique.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "T.hpp" 5 | 6 | using namespace std; 7 | 8 | using T_u_ptr = unique_ptr; 9 | using T_s_ptr = shared_ptr; 10 | 11 | // Creates and gives away explicit ownership... 12 | T_u_ptr source() { return make_unique(); } 13 | 14 | // Assumes explicit ownership, "t" gets deallocated at the end of this function 15 | void sink(T_u_ptr t) { t->foo(); } 16 | 17 | // Does NOT assume explicit ownership because we're taking by reference 18 | void NOT_sink(T_u_ptr& t) { t->foo(); } 19 | 20 | // Assumes ownership, but then hands it back if the caller captures the return value, 21 | // otherwise releases the resource 22 | T_u_ptr sink_or_pass_thru(T_u_ptr t) { t->foo(); return t; } 23 | 24 | // Just a function that takes a shared_ptr... 25 | void shared(T_s_ptr t) { t->foo(); } 26 | 27 | int main() 28 | { 29 | auto t1 = source(); 30 | sink(move(t1)); // We have to std::move it, because copy-constructor of unique_ptr = delete, 31 | // by using std::move we're forcing the use of the move constructor (if one exists), 32 | // this would have worked without std::move if using std::auto_ptr (now deprecated) 33 | // and it would have stole the ownership without warning us!!! 34 | assert(!t1); // "t1" is now pointing to null because of the std::move above 35 | 36 | auto t2 = source(); 37 | NOT_sink(t2); // "t2" still pointing to resource after this call 38 | assert(t2); 39 | sink(move(t2)); // and now "t2" is gone... 40 | assert(!t2); 41 | 42 | sink(source()); // No need for explicit std::move, temporary is captured as r-value reference 43 | // so the unique_ptr's move constructor is automatically invoked 44 | 45 | auto t3 = source(); 46 | auto t4 = sink_or_pass_thru(move(t3)); // Effectively moves the ownership from "t3" to "t4" 47 | assert(!t3 && t4); 48 | 49 | sink_or_pass_thru(source()); // Takes ownership, but deletes the resource since nobody captures the return value 50 | 51 | T_s_ptr t5 = source(); // Create and "upgrade" from unique to shared ownership 52 | T_s_ptr t6 = move(t4); // unique_ptr's must be explicitly std::move'ed to shared_ptr's 53 | assert(!t4 && t5 && t6); 54 | 55 | shared(t6); // No transfer of ownership, just using a shared resource here... 56 | 57 | // PRIMITIVE ARRAYS... 58 | 59 | constexpr const int N = 3; 60 | 61 | auto a1 = make_unique(N); // Allocates N int's, size of array is lost, values are undefined 62 | auto a2 = vector(N, int{42}); // Allocates and value-initializes N int's, size is known, values are well defined 63 | auto a3 = move(a1); // Transfer ownership of from "a1" to "a3" 64 | assert(!a1 && a3); 65 | 66 | a3[N - 1] = 1; // Access the last int of the array 67 | 68 | // ARRAYS... 69 | 70 | auto a4 = make_unique(N); // Create an array of N T's, size is lost, T must have a default constructor 71 | auto a5 = vector(N, T{42}); // Create a vector of N T's, size is known, initialize with custom T 72 | auto a6 = move(a4); // Transfer ownership from "a4" to "a6" 73 | assert(!a4 && a6); 74 | 75 | a6[N - 1].foo(); // Access the last T of the array 76 | } 77 | -------------------------------------------------------------------------------- /src/lrpc_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "lsf.hpp" 10 | #include "socket.hpp" 11 | #include "lrpc_proto.hpp" 12 | 13 | class RPCServer 14 | { 15 | public: 16 | RPCServer(std::uint16_t port) 17 | : m_socket{ port } 18 | { 19 | m_cmd_handlers[cmd_t(CMD::Add)] = [this](client_socket& self, socket_buffer_t data, const host_info_t& hi) 20 | { 21 | auto ut = m_serializer.unpack(data); 22 | auto& packet = std::get<0>(ut); 23 | auto& name = (hi.host.empty() ? hi.ip : hi.host); 24 | 25 | std::cout << "Client (" << name << ") say PAdd(" << int(packet.hdr.seq) << ") " 26 | << std::setfill(' ') << std::setw(3) << packet.num1 << " + " << std::setw(3) << packet.num2 << std::endl; 27 | 28 | auto reply_packet = PAddRep{ { cmd_t(CMD::AddRep), packet.hdr.seq }, packet.num1 + packet.num2 }; 29 | auto reply_data = m_serializer.pack(reply_packet); 30 | 31 | self.send(reply_data.data(), reply_data.size()); 32 | }; 33 | 34 | m_cmd_handlers[cmd_t(CMD::Sub)] = [this](client_socket& self, socket_buffer_t data, const host_info_t& hi) 35 | { 36 | auto ut = m_serializer.unpack(data); 37 | auto& packet = std::get<0>(ut); 38 | auto& name = (hi.host.empty() ? hi.ip : hi.host); 39 | 40 | std::cout << "Client (" << name << ") say PSub(" << int(packet.hdr.seq) << ") " 41 | << std::setfill(' ') << std::setw(3) << packet.num1 << " - " << std::setw(3) << packet.num2 << std::endl; 42 | 43 | auto reply_packet = PSubRep{ { cmd_t(CMD::SubRep), packet.hdr.seq }, packet.num1 - packet.num2 }; 44 | auto reply_data = m_serializer.pack(reply_packet); 45 | 46 | self.send(reply_data.data(), reply_data.size()); 47 | }; 48 | 49 | m_socket.set_accept_handler([this](server_socket& ss, client_socket cs, host_info_t hi) 50 | { 51 | cs.set_data_handler([this, hi = std::move(hi)](client_socket& self, socket_buffer_t data) 52 | { 53 | auto ut = m_serializer.unpack(data); 54 | auto& header = std::get<0>(ut); 55 | auto& handler = m_cmd_handlers.at(header.cmd); 56 | 57 | handler(self, std::move(data), hi); 58 | }); 59 | 60 | std::thread([cs = std::move(cs)]() mutable 61 | { 62 | while(cs.receive()); 63 | }).detach(); 64 | }); 65 | } 66 | 67 | void Run() { while(m_socket.accept()); } 68 | void Stop() { m_socket.close(); } 69 | 70 | private: 71 | server_socket m_socket; 72 | serializer m_serializer; 73 | 74 | using cmd_handler_t = std::function; 75 | using cmd_handler_map_t = std::map; 76 | cmd_handler_map_t m_cmd_handlers; 77 | }; 78 | 79 | int main(int argc, char** argv) 80 | { 81 | using namespace std; 82 | 83 | if(argc != 2) 84 | { 85 | cerr << "USAGE: " << argv[0] << " [port]" << endl; 86 | return 1; 87 | } 88 | 89 | try 90 | { 91 | auto port = uint16_t(stoul(argv[1])); 92 | auto s = RPCServer(port); 93 | 94 | s.Run(); 95 | } 96 | catch(exception& ex) 97 | { 98 | cerr << ex.what() << endl; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /CMakeLists.txt.llvm: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20.1) 2 | 3 | set(CMAKE_TOOLCHAIN_FILE "/Users/martin/Code/vcpkg/scripts/buildsystems/vcpkg.cmake") 4 | 5 | project(blog VERSION 1.0 6 | DESCRIPTION "Vorbrodt's C++ Blog - Code Samples" 7 | HOMEPAGE_URL "https://vorbrodt.blog" 8 | LANGUAGES C CXX) 9 | 10 | set(CMAKE_CXX_STANDARD 20) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | set(CMAKE_CXX_EXTENSIONS OFF) 13 | set(CMAKE_OSX_ARCHITECTURES "x86_64") 14 | 15 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;/usr/local/lib/cmake;/usr/local/lib/cmake/llvm;/usr/local/share/cmake/Modules") 16 | 17 | if(NOT CMAKE_BUILD_TYPE) 18 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Default build type: Release" FORCE) 19 | endif() 20 | 21 | set(OPENSSL_ROOT_DIR "/usr/local/Cellar/openssl@3/3.3.1") 22 | 23 | find_package(Threads REQUIRED) 24 | find_package(TBB CONFIG REQUIRED) 25 | find_package(oneDPL CONFIG REQUIRED) 26 | find_package(Boost CONFIG REQUIRED COMPONENTS serialization stacktrace_basic filesystem system timer program_options) 27 | find_package(Range-v3 CONFIG REQUIRED) 28 | find_package(Catch2 CONFIG REQUIRED) 29 | find_package(Hayai CONFIG REQUIRED) 30 | # find_package(nonius CONFIG REQUIRED) 31 | find_package(FMT CONFIG REQUIRED) 32 | # find_package(cryptopp CONFIG REQUIRED) 33 | 34 | find_package(OpenSSL MODULE REQUIRED) 35 | find_package(MySQL MODULE REQUIRED) 36 | # find_package(Soci MODULE REQUIRED) 37 | find_package(Protobuf MODULE REQUIRED) 38 | find_package(THRIFT MODULE REQUIRED) 39 | find_package(GRPC MODULE REQUIRED) 40 | find_package(LZ4 MODULE REQUIRED) 41 | find_package(XMLRPC MODULE REQUIRED) 42 | find_package(CURL MODULE REQUIRED) 43 | find_package(CURLpp MODULE REQUIRED) 44 | # find_package(BOTAN MODULE REQUIRED) 45 | find_package(WOLFSSL MODULE REQUIRED) 46 | find_package(benchmark MODULE REQUIRED) 47 | 48 | include_directories("/usr/local/include") 49 | link_directories("/usr/local/lib") 50 | 51 | set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 52 | set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 53 | set(MODULE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) 54 | 55 | add_compile_options(-Wall -Wpedantic) 56 | 57 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXXFLAGS}") 58 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") 59 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -ffast-math -march=native") 60 | 61 | function(add_module name) 62 | add_custom_target( 63 | ${name} 64 | COMMAND 65 | ${CMAKE_CXX_COMPILER} 66 | ${CMAKE_CXX_FLAGS} 67 | -std=c++20 68 | -fmodules 69 | -fbuiltin-module-map 70 | -fprebuilt-module-path=${CMAKE_CURRENT_SOURCE_DIR} 71 | -c ${CMAKE_CURRENT_SOURCE_DIR}/${ARGN} 72 | -Xclang -emit-module-interface 73 | -o ${CMAKE_CURRENT_SOURCE_DIR}/${name}.pcm 74 | BYPRODUCTS 75 | ${CMAKE_CURRENT_SOURCE_DIR}/${name}.pcm 76 | ) 77 | add_dependencies(${name} TBB::tbb oneDPL) 78 | endfunction() 79 | 80 | add_subdirectory(src) 81 | 82 | # get_cmake_property(_variableNames VARIABLES) 83 | # list (SORT _variableNames) 84 | # foreach (_variableName ${_variableNames}) 85 | # message(STATUS "${_variableName}=${${_variableName}}") 86 | # endforeach() 87 | -------------------------------------------------------------------------------- /src/erased.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct T1 { int x = 11; }; 9 | struct T2 { int y = 14; }; 10 | struct T3 { int z = 17; }; 11 | 12 | void foo(const T1& t) { std::cout << "foo T1: " << t.x << std::endl; } 13 | void foo(const T2& t) { std::cout << "foo T2: " << t.y << std::endl; } 14 | void foo(const T3& t) { std::cout << "foo T3: " << t.z << std::endl; } 15 | 16 | void bar(const T1& t) { std::cout << "bar T1: " << t.x << std::endl; } 17 | void bar(const T2& t) { std::cout << "bar T2: " << t.y << std::endl; } 18 | void bar(const T3& t) { std::cout << "bar T3: " << t.z << std::endl; } 19 | 20 | class TypeErased 21 | { 22 | public: 23 | template using ByValue = std::remove_cvref_t; 24 | 25 | template requires(not std::same_as>) 26 | TypeErased(T&& v) : value_ptr(std::make_unique>>(std::forward(v))) {} 27 | 28 | TypeErased(const TypeErased& other) : value_ptr(other.value_ptr->Clone()) {} 29 | TypeErased(TypeErased&&) = default; 30 | 31 | TypeErased& operator = (TypeErased other) noexcept 32 | { 33 | swap(other); 34 | return *this; 35 | } 36 | 37 | void swap(TypeErased& other) noexcept { std::swap(value_ptr, other.value_ptr); } 38 | friend void swap(TypeErased& first, TypeErased& second) noexcept { first.swap(second); } 39 | 40 | friend void foo(const TypeErased& erased) { erased.value_ptr->foo(); } 41 | friend void bar(const TypeErased& erased) { erased.value_ptr->bar(); } 42 | 43 | private: 44 | class Concept; 45 | using ConceptPtr = std::unique_ptr; 46 | 47 | class Concept 48 | { 49 | public: 50 | virtual ~Concept() = default; 51 | virtual ConceptPtr Clone() const = 0; 52 | 53 | virtual void foo() const = 0; 54 | virtual void bar() const = 0; 55 | }; 56 | 57 | template 58 | class Model : public Concept 59 | { 60 | public: 61 | Model(const T& value) : my_value(value) {} 62 | Model(T&& value) : my_value(std::move(value)) {} 63 | 64 | virtual ConceptPtr Clone() const override { return std::make_unique(*this); } 65 | 66 | virtual void foo() const override { ::foo(my_value); } 67 | virtual void bar() const override { ::bar(my_value); } 68 | 69 | private: 70 | T my_value; 71 | }; 72 | 73 | ConceptPtr value_ptr; 74 | }; 75 | 76 | using TypeErasedList = std::vector; 77 | 78 | void foo_list(const TypeErasedList& erased_list) { for(const auto& erased : erased_list) foo(erased); } 79 | void bar_list(const TypeErasedList& erased_list) { for(const auto& erased : erased_list) bar(erased); } 80 | 81 | int main() 82 | { 83 | auto e1 = TypeErased{ T1{1} }; 84 | auto e2 = TypeErased{ T2{2} }; 85 | auto e3 = TypeErased{ T3{3} }; 86 | auto e4 = TypeErased{ e1 }; 87 | auto e5 = TypeErased{ std::move(e2) }; 88 | 89 | foo(e3); 90 | foo(e4); 91 | foo(e5); 92 | 93 | bar(e3); 94 | bar(e4); 95 | bar(e5); 96 | 97 | e1 = e4; 98 | e2 = std::move(e5); 99 | e3 = T3{3}; 100 | 101 | auto l = TypeErasedList{ e1, e2, e3 }; 102 | 103 | l.emplace_back(T1{}); 104 | l.emplace_back(T2{}); 105 | l.emplace_back(T3{}); 106 | 107 | foo_list(l); 108 | bar_list(l); 109 | } 110 | -------------------------------------------------------------------------------- /CMakeLists.txt.gcc: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20.1) 2 | 3 | set(CMAKE_TOOLCHAIN_FILE "/Users/martin/Code/vcpkg/scripts/buildsystems/vcpkg.cmake") 4 | 5 | project(blog VERSION 1.0 6 | DESCRIPTION "Vorbrodt's C++ Blog - Code Samples" 7 | HOMEPAGE_URL "https://vorbrodt.blog" 8 | LANGUAGES C CXX) 9 | 10 | set(CMAKE_CXX_STANDARD 20) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | set(CMAKE_CXX_EXTENSIONS OFF) 13 | set(CMAKE_OSX_ARCHITECTURES "x86_64") 14 | 15 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;/usr/local/lib/cmake;/usr/local/lib/cmake/llvm;/usr/local/share/cmake/Modules") 16 | 17 | if(NOT CMAKE_BUILD_TYPE) 18 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Default build type: Release" FORCE) 19 | endif() 20 | 21 | set(OPENSSL_ROOT_DIR "/usr/local/Cellar/openssl@3/3.1.0") 22 | 23 | find_package(Threads REQUIRED) 24 | find_package(TBB CONFIG REQUIRED) 25 | find_package(ParallelSTL CONFIG REQUIRED) 26 | find_package(Boost CONFIG REQUIRED COMPONENTS serialization stacktrace_basic filesystem system timer program_options) 27 | find_package(Range-v3 CONFIG REQUIRED) 28 | find_package(Catch2 CONFIG REQUIRED) 29 | find_package(Hayai CONFIG REQUIRED) 30 | find_package(nonius CONFIG REQUIRED) 31 | find_package(FMT CONFIG REQUIRED) 32 | find_package(cryptopp CONFIG REQUIRED) 33 | 34 | find_package(OpenSSL MODULE REQUIRED) 35 | find_package(MySQL MODULE REQUIRED) 36 | find_package(Soci MODULE REQUIRED) 37 | find_package(Protobuf MODULE REQUIRED) 38 | find_package(THRIFT MODULE REQUIRED) 39 | find_package(GRPC MODULE REQUIRED) 40 | find_package(LZ4 MODULE REQUIRED) 41 | find_package(XMLRPC MODULE REQUIRED) 42 | find_package(CURL MODULE REQUIRED) 43 | find_package(CURLpp MODULE REQUIRED) 44 | find_package(BOTAN MODULE REQUIRED) 45 | find_package(WOLFSSL MODULE REQUIRED) 46 | find_package(benchmark MODULE REQUIRED) 47 | 48 | include_directories("/usr/local/Cellar/gcc/12.2.0/include/c++/12;/usr/local/include") 49 | link_directories("/usr/local/Cellar/gcc/12.2.0/lib/gcc/12;/usr/local/lib") 50 | 51 | set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 52 | set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") 53 | set(MODULE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) 54 | 55 | add_compile_options(-Wall -Wpedantic) 56 | add_link_options(-lstdc++) 57 | 58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXXFLAGS}") 59 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") 60 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -march=native") 61 | 62 | function(add_module name) 63 | add_custom_target( 64 | ${name} 65 | COMMAND 66 | ${CMAKE_CXX_COMPILER} 67 | ${CMAKE_CXX_FLAGS} 68 | -std=c++20 69 | -fmodules 70 | -fbuiltin-module-map 71 | -fprebuilt-module-path=${CMAKE_CURRENT_SOURCE_DIR} 72 | -c ${CMAKE_CURRENT_SOURCE_DIR}/${ARGN} 73 | -Xclang -emit-module-interface 74 | -o ${CMAKE_CURRENT_SOURCE_DIR}/${name}.pcm 75 | BYPRODUCTS 76 | ${CMAKE_CURRENT_SOURCE_DIR}/${name}.pcm 77 | ) 78 | add_dependencies(${name} TBB::tbb oneDPL) 79 | endfunction() 80 | 81 | add_subdirectory(src) 82 | 83 | # get_cmake_property(_variableNames VARIABLES) 84 | # list (SORT _variableNames) 85 | # foreach (_variableName ${_variableNames}) 86 | # message(STATUS "${_variableName}=${${_variableName}}") 87 | # endforeach() 88 | -------------------------------------------------------------------------------- /src/pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "pool.hpp" 10 | #include "ansi_escape_code.hpp" 11 | 12 | using namespace std; 13 | using namespace std::chrono; 14 | using namespace ansi_escape_code; 15 | 16 | std::string with_commas(uint64_t value) 17 | { 18 | std::stringstream ss; 19 | ss.imbue(std::locale("")); 20 | ss << std::fixed << value; 21 | return ss.str(); 22 | } 23 | 24 | template 25 | void benchmark(bool fast_path, const char* pool_name, uint64_t tasks, uint64_t reps, std::size_t pool_threads, std::size_t push_threads) 26 | { 27 | cout << reset << bold << pool_name << normal << " (" << red << with_commas(tasks) << reset << " tasks, " << red << with_commas(reps) << reset << " reps)" << flush; 28 | 29 | std::atomic_uint check{}; 30 | auto work = [&](uint64_t r) 31 | { 32 | volatile uint32_t sum = 0; 33 | while (r--) 34 | #ifdef _MSC_VER 35 | ++sum; 36 | #else 37 | asm volatile("inc %0\n\t" : "+r" (sum)); 38 | #endif 39 | check.fetch_add(sum, std::memory_order_relaxed); 40 | }; 41 | 42 | auto start_time = high_resolution_clock::now(); 43 | { 44 | PT pool{ pool_threads }; 45 | 46 | using threads = std::vector; 47 | threads ts; 48 | 49 | for(int i = 0; i < push_threads; ++i) 50 | { 51 | ts.emplace_back([&] 52 | { 53 | for (uint64_t i = 1; i <= tasks; ++i) 54 | { 55 | if(fast_path) pool.enqueue_work(work, reps); 56 | else [[maybe_unused]] auto p = pool.enqueue_task(work, reps); 57 | } 58 | }); 59 | } 60 | 61 | for(auto& t : ts) 62 | t.join(); 63 | } 64 | auto end_time = high_resolution_clock::now(); 65 | 66 | auto good = (check == tasks * reps * push_threads); 67 | cout << "\t" << red << duration_cast(end_time - start_time).count() / 1000.f << " ms" << "\t" << reset; 68 | if(good) cout << "(" << green << with_commas(check) << reset << ")"; 69 | else cout << "(" << red << with_commas(check) << reset << ")";wcout << blue; 70 | cout << endl; 71 | } 72 | 73 | int main() 74 | { 75 | auto cores = std::thread::hardware_concurrency(); 76 | 77 | uint64_t TASK_START = 0; 78 | uint64_t TASK_STEP = 250'000; 79 | uint64_t TASK_STOP = 1'000'000; 80 | 81 | uint64_t REPS_START = 0; 82 | uint64_t REPS_STEP = 25; 83 | uint64_t REPS_STOP = 100; 84 | 85 | for(auto t = TASK_START; t <= TASK_STOP; t += TASK_STEP) 86 | { 87 | if(TASK_START < TASK_STOP) 88 | cout << "********************************************************************************" << endl; 89 | 90 | for(auto r = REPS_START; r <= REPS_STOP; r += REPS_STEP) 91 | { 92 | benchmark(true, "S/fast", !t ? 100'000 : t, !r ? 1 : r, cores, cores / 2); 93 | benchmark (true, "A/fast", !t ? 100'000 : t, !r ? 1 : r, cores, cores / 2); 94 | // cout << endl; 95 | // benchmark(false, "S/slow", !t ? 100'000 : t, !r ? 1 : r, cores, cores / 2); 96 | // benchmark (false, "A/slow", !t ? 100'000 : t, !r ? 1 : r, cores, cores / 2); 97 | 98 | if(REPS_START < REPS_STOP) 99 | cout << endl; 100 | } 101 | 102 | if(TASK_START < TASK_STOP) 103 | cout << endl; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/lesson_memory_pool_how_to.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | template 17 | class memory_pool 18 | { 19 | public: 20 | static_assert(ChunkSize >= sizeof(void*)); 21 | static_assert(ChunksPerBlock > 0); 22 | 23 | explicit memory_pool() = default; 24 | 25 | memory_pool(const memory_pool&) = delete; 26 | memory_pool(memory_pool&& other) 27 | {} 28 | 29 | memory_pool& operator = (memory_pool other) 30 | { 31 | return *this; 32 | } 33 | 34 | ~memory_pool() 35 | { 36 | for(auto ptr : m_blocks) 37 | std::free(ptr); 38 | } 39 | 40 | void* malloc() 41 | { 42 | if(m_next_chunk == nullptr) 43 | m_next_chunk = allocate_block(nullptr); 44 | chunk_t* chunk = m_next_chunk; 45 | m_next_chunk = m_next_chunk->m_next; 46 | cout << "malloc chunk #" << setw(2) << setfill('0') << chunk->m_seq << " " << chunk << endl; 47 | return chunk; 48 | } 49 | 50 | void free(void* ptr) 51 | { 52 | chunk_t* chunk = reinterpret_cast(ptr); 53 | chunk->m_next = m_next_chunk; 54 | m_next_chunk = chunk; 55 | cout << "free chunk #" << setw(2) << setfill('0') << chunk->m_seq << " " << chunk << endl; 56 | } 57 | 58 | void reserve_blocks(std::size_t blocks) 59 | { 60 | while(blocks > m_blocks.size()) 61 | m_next_chunk = allocate_block(m_next_chunk); 62 | } 63 | 64 | private: 65 | struct chunk_t 66 | { 67 | chunk_t(chunk_t* next = nullptr) : m_next{ next } 68 | { 69 | cout << "create chunk #" << setw(2) << setfill('0') << m_seq << " " << this << " -> " << m_next << endl; 70 | } 71 | 72 | union 73 | { 74 | chunk_t* m_next; 75 | std::byte m_storage[ChunkSize]; 76 | }; 77 | 78 | std::size_t m_seq = ++k_seq; 79 | inline static std::size_t k_seq = 0; 80 | }; 81 | 82 | chunk_t* allocate_block(chunk_t* tail) 83 | { 84 | unique_lock guard(m_lock); 85 | if(m_next_chunk != nullptr) 86 | return m_next_chunk; 87 | 88 | chunk_t* block = reinterpret_cast(std::malloc(sizeof(chunk_t) * ChunksPerBlock)); 89 | if(block == nullptr) throw std::bad_alloc(); 90 | m_blocks.push_back(block); 91 | 92 | chunk_t* chunk = block; 93 | 94 | for(std::size_t i = 0; i < ChunksPerBlock - 1; ++i) 95 | { 96 | new (chunk) chunk_t{ chunk + 1 }; 97 | chunk = chunk->m_next; 98 | } 99 | new (chunk) chunk_t{ tail }; 100 | 101 | return block; 102 | } 103 | 104 | chunk_t* m_next_chunk = nullptr; 105 | 106 | std::mutex m_lock; 107 | using block_list_t = std::list; 108 | block_list_t m_blocks; 109 | }; 110 | 111 | int main() 112 | { 113 | constexpr auto Chunks = 6; 114 | 115 | memory_pool pool; 116 | vector chunks; 117 | 118 | pool.reserve_blocks(1); 119 | 120 | for(auto i = 0; i < Chunks; ++i) 121 | { 122 | void* chunk = pool.malloc(); 123 | chunks.push_back(chunk); 124 | } 125 | 126 | random_device dev; 127 | mt19937 gen{ dev() }; 128 | 129 | shuffle(chunks.begin(), chunks.end(), gen); 130 | 131 | for(auto chunk : chunks) 132 | pool.free(chunk); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /cmake/FindSOCI.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # CMake module to search for SOCI library 3 | # 4 | # WARNING: This module is experimental work in progress. 5 | # 6 | # This module defines: 7 | # SOCI_INCLUDE_DIRS = include dirs to be used when using the soci library 8 | # SOCI_LIBRARY = full path to the soci library 9 | # SOCI_VERSION = the soci version found (not yet. soci does not provide that info.) 10 | # SOCI_FOUND = true if soci was found 11 | # 12 | # This module respects: 13 | # LIB_SUFFIX = (64|32|"") Specifies the suffix for the lib directory 14 | # 15 | # For each component you specify in find_package(), the following variables are set. 16 | # 17 | # SOCI_${COMPONENT}_PLUGIN = full path to the soci plugin 18 | # SOCI_${COMPONENT}_FOUND 19 | # 20 | # Copyright (c) 2011 Michael Jansen 21 | # 22 | # Redistribution and use is allowed according to the terms of the BSD license. 23 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 24 | # 25 | ############################################################################### 26 | # 27 | ### Global Configuration Section 28 | # 29 | SET(_SOCI_ALL_PLUGINS mysql odbc postgresql sqlite3) 30 | SET(_SOCI_REQUIRED_VARS SOCI_INCLUDE_DIR SOCI_LIBRARY) 31 | 32 | # 33 | ### FIRST STEP: Find the soci headers. 34 | # 35 | FIND_PATH( 36 | SOCI_INCLUDE_DIR soci.h 37 | PATH "/usr/local" 38 | PATH_SUFFIXES "" "soci" 39 | DOC "Soci (http://soci.sourceforge.net) include directory") 40 | MARK_AS_ADVANCED(SOCI_INCLUDE_DIR) 41 | 42 | SET(SOCI_INCLUDE_DIRS ${SOCI_INCLUDE_DIR}) 43 | 44 | # 45 | ### SECOND STEP: Find the soci core library. Respect LIB_SUFFIX 46 | # 47 | FIND_LIBRARY( 48 | SOCI_LIBRARY 49 | NAMES soci_core 50 | HINTS ${SOCI_INCLUDE_DIR}/.. 51 | PATH_SUFFIXES lib${LIB_SUFFIX}) 52 | MARK_AS_ADVANCED(SOCI_LIBRARY) 53 | 54 | GET_FILENAME_COMPONENT(SOCI_LIBRARY_DIR ${SOCI_LIBRARY} PATH) 55 | MARK_AS_ADVANCED(SOCI_LIBRARY_DIR) 56 | 57 | # 58 | ### THIRD STEP: Find all installed plugins if the library was found 59 | # 60 | IF(SOCI_INCLUDE_DIR AND SOCI_LIBRARY) 61 | 62 | MESSAGE(STATUS "Soci found: Looking for plugins") 63 | FOREACH(plugin IN LISTS _SOCI_ALL_PLUGINS) 64 | 65 | FIND_LIBRARY( 66 | SOCI_${plugin}_PLUGIN 67 | NAMES soci_${plugin} 68 | HINTS ${SOCI_INCLUDE_DIR}/.. 69 | PATH_SUFFIXES lib${LIB_SUFFIX}) 70 | MARK_AS_ADVANCED(SOCI_${plugin}_PLUGIN) 71 | 72 | IF(SOCI_${plugin}_PLUGIN) 73 | MESSAGE(STATUS " * Plugin ${plugin} found ${SOCI_${plugin}_PLUGIN}.") 74 | SET(SOCI_${plugin}_FOUND True) 75 | ELSE() 76 | MESSAGE(STATUS " * Plugin ${plugin} not found.") 77 | SET(SOCI_${plugin}_FOUND False) 78 | ENDIF() 79 | 80 | ENDFOREACH() 81 | 82 | # 83 | ### FOURTH CHECK: Check if the required components were all found 84 | # 85 | FOREACH(component ${Soci_FIND_COMPONENTS}) 86 | IF(NOT SOCI_${component}_FOUND) 87 | MESSAGE(SEND_ERROR "Required component ${component} not found.") 88 | ENDIF() 89 | ENDFOREACH() 90 | 91 | ENDIF() 92 | 93 | # 94 | ### ADHERE TO STANDARDS 95 | # 96 | include(FindPackageHandleStandardArgs) 97 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Soci DEFAULT_MSG ${_SOCI_REQUIRED_VARS}) 98 | -------------------------------------------------------------------------------- /cmake/FindThrift.cmake: -------------------------------------------------------------------------------- 1 | # - Find Thrift (a cross platform RPC lib/tool) 2 | # This module defines 3 | # THRIFT_VERSION_STRING, version string of ant if found 4 | # THRIFT_LIBRARIES, libraries to link 5 | # THRIFT_INCLUDE_DIR, where to find THRIFT headers 6 | # THRIFT_COMPILER, thrift compiler executable 7 | # THRIFT_FOUND, If false, do not try to use ant 8 | # Function 9 | # thrift_gen_cpp( ) 10 | # 11 | # Initial work was done by Cloudera https://github.com/cloudera/Impala 12 | # 2014 - modified by snikulov 13 | 14 | # prefer the thrift version supplied in THRIFT_HOME (cmake -DTHRIFT_HOME then environment) 15 | find_path(THRIFT_INCLUDE_DIR 16 | NAMES 17 | thrift/Thrift.h 18 | HINTS 19 | ${THRIFT_HOME} 20 | ENV THRIFT_HOME 21 | /usr/local 22 | /opt/local 23 | PATH_SUFFIXES 24 | include 25 | ) 26 | 27 | # prefer the thrift version supplied in THRIFT_HOME 28 | find_library(THRIFT_LIBRARIES 29 | NAMES 30 | thrift libthrift 31 | HINTS 32 | ${THRIFT_HOME} 33 | ENV THRIFT_HOME 34 | /usr/local 35 | /opt/local 36 | PATH_SUFFIXES 37 | lib lib64 38 | ) 39 | 40 | find_program(THRIFT_COMPILER 41 | NAMES 42 | thrift 43 | HINTS 44 | ${THRIFT_HOME} 45 | ENV THRIFT_HOME 46 | /usr/local 47 | /opt/local 48 | PATH_SUFFIXES 49 | bin bin64 50 | ) 51 | 52 | if (THRIFT_COMPILER) 53 | exec_program(${THRIFT_COMPILER} 54 | ARGS -version OUTPUT_VARIABLE __thrift_OUT RETURN_VALUE THRIFT_RETURN) 55 | string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+-[a-z]+$" THRIFT_VERSION_STRING ${__thrift_OUT}) 56 | 57 | # define utility function to generate cpp files 58 | function(thrift_gen_cpp thrift_file THRIFT_CPP_FILES_LIST THRIFT_GEN_INCLUDE_DIR) 59 | set(_res) 60 | set(_res_inc_path) 61 | if(EXISTS ${thrift_file}) 62 | get_filename_component(_target_dir ${thrift_file} NAME_WE) 63 | message("thrif_gen_cpp: ${thrift_file}") 64 | 65 | if(NOT EXISTS ${CMAKE_BINARY_DIR}/${_target_dir}) 66 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${_target_dir}) 67 | endif() 68 | exec_program(${THRIFT_COMPILER} 69 | ARGS -o "${CMAKE_BINARY_DIR}/${_target_dir}" --gen cpp ${thrift_file} 70 | OUTPUT_VARIABLE __thrift_OUT 71 | RETURN_VALUE THRIFT_RETURN) 72 | file(GLOB_RECURSE __result_src "${CMAKE_BINARY_DIR}/${_target_dir}/*.cpp") 73 | file(GLOB_RECURSE __result_hdr "${CMAKE_BINARY_DIR}/${_target_dir}/*.h") 74 | list(APPEND _res ${__result_src}) 75 | list(APPEND _res ${__result_hdr}) 76 | if(__result_hdr) 77 | list(GET __result_hdr 0 _res_inc_path) 78 | get_filename_component(_res_inc_path ${_res_inc_path} DIRECTORY) 79 | endif() 80 | else() 81 | message("thrift_gen_cpp: file ${thrift_file} does not exists") 82 | endif() 83 | set(${THRIFT_CPP_FILES_LIST} "${_res}" PARENT_SCOPE) 84 | set(${THRIFT_GEN_INCLUDE_DIR} "${_res_inc_path}" PARENT_SCOPE) 85 | endfunction() 86 | endif () 87 | 88 | 89 | include(FindPackageHandleStandardArgs) 90 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(THRIFT DEFAULT_MSG THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER) 91 | mark_as_advanced(THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER THRIFT_VERSION_STRING) 92 | -------------------------------------------------------------------------------- /src/base64.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace base64 9 | { 10 | inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 11 | inline static const char kPadCharacter = '='; 12 | 13 | using byte = std::uint8_t; 14 | 15 | inline std::string encode(const std::vector& input) 16 | { 17 | std::string encoded; 18 | encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4); 19 | 20 | std::uint32_t temp{}; 21 | auto it = input.begin(); 22 | 23 | for(std::size_t i = 0; i < input.size() / 3; ++i) 24 | { 25 | temp = (*it++) << 16; 26 | temp += (*it++) << 8; 27 | temp += (*it++); 28 | encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); 29 | encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); 30 | encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]); 31 | encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]); 32 | } 33 | 34 | switch(input.size() % 3) 35 | { 36 | case 1: 37 | temp = (*it++) << 16; 38 | encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); 39 | encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); 40 | encoded.append(2, kPadCharacter); 41 | break; 42 | case 2: 43 | temp = (*it++) << 16; 44 | temp += (*it++) << 8; 45 | encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); 46 | encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); 47 | encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]); 48 | encoded.append(1, kPadCharacter); 49 | break; 50 | } 51 | 52 | return encoded; 53 | } 54 | 55 | inline std::vector decode(const std::string& input) 56 | { 57 | if(input.length() % 4) 58 | throw std::runtime_error("Invalid base64 length!"); 59 | 60 | std::size_t padding{}; 61 | 62 | if(input.length()) 63 | { 64 | if(input[input.length() - 1] == kPadCharacter) padding++; 65 | if(input[input.length() - 2] == kPadCharacter) padding++; 66 | } 67 | 68 | std::vector decoded; 69 | decoded.reserve(((input.length() / 4) * 3) - padding); 70 | 71 | std::uint32_t temp{}; 72 | auto it = input.begin(); 73 | 74 | while(it < input.end()) 75 | { 76 | for(std::size_t i = 0; i < 4; ++i) 77 | { 78 | temp <<= 6; 79 | if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; 80 | else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; 81 | else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; 82 | else if(*it == 0x2B) temp |= 0x3E; 83 | else if(*it == 0x2F) temp |= 0x3F; 84 | else if(*it == kPadCharacter) 85 | { 86 | switch(input.end() - it) 87 | { 88 | case 1: 89 | decoded.push_back((temp >> 16) & 0x000000FF); 90 | decoded.push_back((temp >> 8 ) & 0x000000FF); 91 | return decoded; 92 | case 2: 93 | decoded.push_back((temp >> 10) & 0x000000FF); 94 | return decoded; 95 | default: 96 | throw std::runtime_error("Invalid padding in base64!"); 97 | } 98 | } 99 | else throw std::runtime_error("Invalid character in base64!"); 100 | 101 | ++it; 102 | } 103 | 104 | decoded.push_back((temp >> 16) & 0x000000FF); 105 | decoded.push_back((temp >> 8 ) & 0x000000FF); 106 | decoded.push_back((temp ) & 0x000000FF); 107 | } 108 | 109 | return decoded; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/deep_ptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | struct default_cloner 12 | { 13 | using pointer = T*; 14 | pointer operator () (pointer p) const 15 | { 16 | return (p ? new T{ *p } : nullptr); 17 | } 18 | }; 19 | 20 | template, typename D = std::default_delete> 21 | class deep_ptr 22 | { 23 | public: 24 | using pointer = T*; 25 | using element_type = T; 26 | using reference_type = T&; 27 | using cloner_type = C; 28 | using deleter_type = D; 29 | 30 | constexpr deep_ptr() noexcept = default; 31 | constexpr deep_ptr(std::nullptr_t) noexcept {} 32 | 33 | explicit deep_ptr(pointer p) noexcept : m_p{ p } {} 34 | 35 | deep_ptr(pointer p, cloner_type c) : m_c{ c }, m_p{ p } {} 36 | deep_ptr(pointer p, deleter_type d) : m_d{ d }, m_p{ p } {} 37 | deep_ptr(pointer p, cloner_type c, deleter_type d) : m_c{ c }, m_d{ d }, m_p{ p } {} 38 | 39 | deep_ptr(const deep_ptr& d) : m_c{ d.m_c }, m_d{ d.m_d }, m_p{ get_cloner()(d.m_p) } {} 40 | deep_ptr(deep_ptr&& d) noexcept : m_c{ std::move(d.m_c) }, m_d{ std::move(d.m_d) }, m_p{ std::exchange(d.m_p, nullptr) } {} 41 | 42 | template 43 | deep_ptr(const deep_ptr& d) : m_c{ d.m_c }, m_d{ d.m_d }, m_p{ get_cloner()(d.m_p) } {} 44 | 45 | template 46 | deep_ptr(deep_ptr&& d) noexcept : m_c{ std::move(d.m_c) }, m_d{ std::move(d.m_d) }, m_p{ std::exchange(d.m_p, nullptr) } {} 47 | 48 | ~deep_ptr() noexcept { get_deleter()(get()); } 49 | 50 | deep_ptr& operator = (deep_ptr r) noexcept 51 | { 52 | swap(r); 53 | return *this; 54 | } 55 | 56 | friend bool operator == (const deep_ptr& x, const deep_ptr& y) noexcept { return x.m_p == y.m_p; } 57 | 58 | friend auto operator <=> (const deep_ptr& x, const deep_ptr& y) noexcept { return x.m_p <=> y.m_p; } 59 | 60 | explicit operator bool () const noexcept { return m_p != nullptr; } 61 | 62 | reference_type operator * () const { return *m_p; } 63 | 64 | pointer operator -> () const noexcept { return m_p; } 65 | 66 | pointer get() const noexcept { return m_p; } 67 | 68 | deleter_type& get_deleter() noexcept { return m_d; } 69 | 70 | const deleter_type& get_deleter() const noexcept { return m_d; } 71 | 72 | cloner_type& get_cloner() noexcept { return m_c; } 73 | 74 | const cloner_type& get_cloner() const noexcept { return m_c; } 75 | 76 | pointer release() noexcept { return std::exchange(m_p, nullptr); } 77 | 78 | void reset(pointer p = pointer()) noexcept { get_deleter()(std::exchange(m_p, p)); } 79 | 80 | void swap(deep_ptr& o) noexcept 81 | { 82 | std::swap(m_c, o.m_c); 83 | std::swap(m_d, o.m_d); 84 | std::swap(m_p, o.m_p); 85 | } 86 | 87 | private: 88 | template friend class deep_ptr; 89 | 90 | cloner_type m_c = cloner_type(); 91 | deleter_type m_d = deleter_type(); 92 | pointer m_p = pointer(); 93 | }; 94 | 95 | template 96 | inline void swap(deep_ptr& x, deep_ptr& y) 97 | { 98 | x.swap(y); 99 | } 100 | 101 | template 102 | inline auto make_deep(std::initializer_list l) 103 | { 104 | using U = std::decay_t; 105 | return deep_ptr(new U(l)); 106 | } 107 | 108 | template 109 | inline auto make_deep(A&&... a) 110 | { 111 | return deep_ptr(new T(std::forward(a)...)); 112 | } 113 | -------------------------------------------------------------------------------- /src/mutex.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * fast_mutex designed by Alexander Terekhov, implemented by Chris Thomasson 3 | * rw_fast_mutex designed and implemented by Chris Thomasson 4 | * 5 | * https://sourceware.org/pthreads-win32/ 6 | * 7 | * https://software.intel.com/en-us/forums/intel-threading-building-blocks/topic/296471 8 | * 9 | * https://groups.google.com/forum/#!topic/comp.lang.c++/g6veUOvOAu0 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "event.hpp" 20 | #include "semaphore.hpp" 21 | 22 | 23 | 24 | class simple_spinlock_mutex 25 | { 26 | public: 27 | void lock() 28 | { 29 | while(flag.test_and_set(std::memory_order_acquire)); 30 | } 31 | 32 | void unlock() 33 | { 34 | flag.clear(std::memory_order_release); 35 | } 36 | 37 | private: 38 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 39 | }; 40 | 41 | 42 | 43 | class spinlock_mutex 44 | { 45 | public: 46 | void lock() 47 | { 48 | while(true) 49 | { 50 | if(!m_lock.exchange(true, std::memory_order_acquire)) 51 | break; 52 | while(m_lock.load(std::memory_order_relaxed)) 53 | std::this_thread::yield(); 54 | //asm volatile("pause"); 55 | } 56 | } 57 | 58 | bool try_lock() 59 | { 60 | return !m_lock.load(std::memory_order_relaxed) && !m_lock.exchange(true, std::memory_order_acquire); 61 | } 62 | 63 | void unlock() 64 | { 65 | m_lock.store(false, std::memory_order_release); 66 | } 67 | 68 | private: 69 | #ifdef __cpp_lib_hardware_interference_size 70 | alignas(std::hardware_destructive_interference_size) std::atomic_bool m_lock = false; 71 | #else 72 | alignas(2 * sizeof(std::max_align_t)) std::atomic_bool m_lock = false; 73 | #endif 74 | }; 75 | 76 | 77 | 78 | class fast_mutex 79 | { 80 | public: 81 | fast_mutex() : m_state(0) {} 82 | 83 | void lock() 84 | { 85 | if (m_state.exchange(1, std::memory_order_acquire)) 86 | while (m_state.exchange(2, std::memory_order_acquire)) 87 | m_waitset.wait(); 88 | } 89 | 90 | void unlock() 91 | { 92 | if (m_state.exchange(0, std::memory_order_release) == 2) 93 | m_waitset.signal(); 94 | } 95 | 96 | private: 97 | std::atomic_uint m_state; 98 | auto_event m_waitset; 99 | }; 100 | 101 | 102 | 103 | class rw_fast_mutex 104 | { 105 | public: 106 | rw_fast_mutex() 107 | : m_wrstate(1), m_count(INT_MAX), m_rdwake(0), 108 | m_rdwset(0), m_wrwset(0), m_wrmtx(0) {} 109 | 110 | void read_lock() 111 | { 112 | if (m_count.fetch_add(-1, std::memory_order_acquire) < 1) 113 | m_rdwset.wait(); 114 | } 115 | 116 | void read_unlock() 117 | { 118 | if (m_count.fetch_add(1, std::memory_order_release) < 0) 119 | if (m_rdwake.fetch_add(-1, std::memory_order_acq_rel) == 1) 120 | m_wrwset.post(); 121 | } 122 | 123 | void write_lock() 124 | { 125 | if (m_wrstate.fetch_sub(1, std::memory_order_acquire) < 1) 126 | m_wrmtx.wait(); 127 | int count = m_count.fetch_add(-INT_MAX, std::memory_order_acquire); 128 | if (count < INT_MAX) 129 | { 130 | int rdwake = m_rdwake.fetch_add(INT_MAX - count, std::memory_order_acquire); 131 | if (rdwake + INT_MAX - count) 132 | m_wrwset.wait(); 133 | } 134 | } 135 | 136 | void write_unlock() 137 | { 138 | int count = m_count.fetch_add(INT_MAX, std::memory_order_release); 139 | if (count < 0) 140 | m_rdwset.post(-count); 141 | if (m_wrstate.fetch_add(1, std::memory_order_release) < 0) 142 | m_wrmtx.post(); 143 | } 144 | 145 | private: 146 | std::atomic_int m_wrstate; 147 | std::atomic_int m_count; 148 | std::atomic_int m_rdwake; 149 | 150 | semaphore m_rdwset; 151 | semaphore m_wrwset; 152 | semaphore m_wrmtx; 153 | }; 154 | -------------------------------------------------------------------------------- /src/lesson_spinlock_event_semaphore_how_to.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | 10 | class spinlock 11 | { 12 | public: 13 | void lock() 14 | { 15 | while(m_flag.test_and_set()); 16 | } 17 | 18 | void unlock() 19 | { 20 | m_flag.clear(); 21 | } 22 | 23 | private: 24 | std::atomic_flag m_flag = ATOMIC_FLAG_INIT; 25 | }; 26 | 27 | 28 | 29 | class manual_event 30 | { 31 | public: 32 | explicit manual_event(bool state = false) : m_signaled{ state } {} 33 | 34 | void signal() 35 | { 36 | { 37 | std::scoped_lock guard{ m_mutex }; 38 | m_signaled = true; 39 | } 40 | m_cv.notify_all(); 41 | } 42 | 43 | void wait() 44 | { 45 | std::unique_lock guard{ m_mutex }; 46 | m_cv.wait(guard, [this] { return m_signaled; }); 47 | } 48 | 49 | private: 50 | bool m_signaled; 51 | std::mutex m_mutex; 52 | std::condition_variable m_cv; 53 | }; 54 | 55 | 56 | 57 | class auto_event 58 | { 59 | public: 60 | explicit auto_event(bool state = false) : m_signaled{ state } {} 61 | 62 | void signal() 63 | { 64 | { 65 | std::scoped_lock guard{ m_mutex }; 66 | m_signaled = true; 67 | } 68 | m_cv.notify_one(); 69 | } 70 | 71 | void wait() 72 | { 73 | std::unique_lock guard{ m_mutex }; 74 | m_cv.wait(guard, [this] { return m_signaled; }); 75 | m_signaled = false; 76 | } 77 | 78 | private: 79 | bool m_signaled; 80 | std::mutex m_mutex; 81 | std::condition_variable m_cv; 82 | // here you can use your spinlock instead: 83 | // spinlock m_mutex; 84 | // std::condition_variable_any m_cv; 85 | }; 86 | 87 | 88 | 89 | class sempahore 90 | { 91 | public: 92 | explicit sempahore(unsigned int count = 0) : m_count{ count } {} 93 | 94 | void post() 95 | { 96 | { 97 | std::scoped_lock guard{ m_mutex }; 98 | m_count = m_count + 1; 99 | } 100 | m_cv.notify_one(); 101 | } 102 | 103 | void post(unsigned int count) 104 | { 105 | if(!count) 106 | throw std::invalid_argument("don't be silly!"); 107 | 108 | { 109 | std::scoped_lock guard{ m_mutex }; 110 | m_count = m_count + count; 111 | } 112 | m_cv.notify_all(); 113 | } 114 | 115 | void wait() 116 | { 117 | std::unique_lock guard{ m_mutex }; 118 | m_cv.wait(guard, [this] { return m_count > 0; }); 119 | m_count = m_count - 1; 120 | } 121 | 122 | private: 123 | unsigned int m_count; 124 | std::mutex m_mutex; 125 | std::condition_variable m_cv; 126 | }; 127 | 128 | 129 | 130 | int main() 131 | { 132 | using namespace std; 133 | using namespace std::chrono; 134 | /* 135 | spinlock s1, s2; 136 | manual_event e1, e2; 137 | 138 | for(int i = 1; i <= 10; ++i) 139 | { 140 | thread([&](int x) 141 | { 142 | cout << "starting " << i << endl; 143 | e1.wait(); 144 | cout << x << endl; 145 | }, i).detach(); 146 | this_thread::sleep_for(10ms); 147 | } 148 | 149 | this_thread::sleep_for(1s); 150 | e1.signal(); 151 | this_thread::sleep_for(1s); 152 | 153 | 154 | 155 | auto_event ae1, ae2; 156 | 157 | thread([&] 158 | { 159 | while(true) 160 | { 161 | ae1.wait(); 162 | cout << "A"; 163 | ae2.signal(); 164 | } 165 | }).detach(); 166 | 167 | thread([&] 168 | { 169 | while(true) 170 | { 171 | ae2.wait(); 172 | cout << "B"; 173 | ae1.signal(); 174 | } 175 | }).detach(); 176 | 177 | ae1.signal(); 178 | 179 | this_thread::sleep_for(1ms); 180 | */ 181 | 182 | 183 | sempahore sem1{ 2 }; 184 | 185 | for(int i = 0; i < 10; ++i) 186 | { 187 | thread([&] (int x) 188 | { 189 | sem1.wait(); 190 | cout << x; 191 | cout.flush(); 192 | }, i).detach(); 193 | } 194 | 195 | for(int i = 0; i < 8 / 2; ++i) 196 | { 197 | this_thread::sleep_for(1s); 198 | sem1.post(2); 199 | } 200 | 201 | this_thread::sleep_for(1s); 202 | 203 | std::mutex sm1, sm2, sm3; 204 | 205 | std::scoped_lock guard{ sm1, sm2, sm3 }; 206 | } 207 | -------------------------------------------------------------------------------- /src/class.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | #define VIRTUAL_FUNCTION_1 0 9 | #define VIRTUAL_FUNCTION_2 1 10 | 11 | typedef void(*VirtualFunctionPtr)(void*, int arg); 12 | 13 | struct Base 14 | { 15 | static void BaseConstructor(void* _this_) 16 | { 17 | ((Base*)_this_)->VirtualTablePtr = BaseVirtualTable; 18 | ((Base*)_this_)->Member1 = rand() % 100; 19 | cout << "BaseConstructor(" << _this_ << 20 | ", Member1 = " << ((Base*)_this_)->Member1 << ")" << endl; 21 | } 22 | 23 | static void BaseDestructor(void* _this_) 24 | { 25 | cout << "BaseDestructor(" << _this_ << ")" << endl; 26 | } 27 | 28 | static void BaseVirtualFunction_1(void* _this_, int arg) 29 | { 30 | cout << "Base(Member1 = " << ((Base*)_this_)->Member1 << 31 | ")::BaseVirtualFunction_1(" << arg << ")" << endl; 32 | } 33 | 34 | static void BaseVirtualFunction_2(void* _this_, int arg) 35 | { 36 | cout << "Base(Member1 = " << ((Base*)_this_)->Member1 << 37 | ")::BaseVirtualFunction_2(" << arg << ")" << endl; 38 | } 39 | 40 | VirtualFunctionPtr* VirtualTablePtr; 41 | int Member1; 42 | static VirtualFunctionPtr BaseVirtualTable[3]; 43 | }; 44 | 45 | VirtualFunctionPtr Base::BaseVirtualTable[3] = 46 | { 47 | &Base::BaseVirtualFunction_1, 48 | &Base::BaseVirtualFunction_2 49 | }; 50 | 51 | struct Derived 52 | { 53 | static void DerivedConstructor(void* _this_) 54 | { 55 | Base::BaseConstructor(_this_); 56 | ((Derived*)_this_)->VirtualTablePtr = DerivedVirtualTable; 57 | ((Derived*)_this_)->Member2 = rand() % 100; 58 | cout << "DerivedConstructor(" << _this_ << 59 | ", Member2 = " << ((Derived*)_this_)->Member2 << ")" << endl; 60 | } 61 | 62 | static void DerivedDestructor(void* _this_) 63 | { 64 | cout << "DerivedDestructor(" << _this_ << ")" << endl; 65 | Base::BaseDestructor(_this_); 66 | } 67 | 68 | static void DerivedVirtualFunction_1(void* _this_, int arg) 69 | { 70 | cout << "Derived(Member1 = " << ((Base*)_this_)->Member1 << 71 | ", Member2 = " << ((Derived*)_this_)->Member2 << 72 | ")::DerivedVirtualFunction_1(" << arg << ")" << endl; 73 | } 74 | 75 | static void DerivedVirtualFunction_2(void* _this_, int arg) 76 | { 77 | cout << "Derived(Member1 = " << ((Base*)_this_)->Member1 << 78 | ", Member2 = " << ((Derived*)_this_)->Member2 << 79 | ")::DerivedVirtualFunction_2(" << arg << ")" << endl; 80 | } 81 | 82 | VirtualFunctionPtr* VirtualTablePtr; 83 | int __Space_for_Base_Member1__; 84 | int Member2; 85 | static VirtualFunctionPtr DerivedVirtualTable[3]; 86 | }; 87 | 88 | VirtualFunctionPtr Derived::DerivedVirtualTable[3] = 89 | { 90 | &Derived::DerivedVirtualFunction_1, 91 | &Derived::DerivedVirtualFunction_2 92 | }; 93 | 94 | template 95 | void VirtualDispatch(T* _this_, int VirtualFuncNum, A&&... args) 96 | { 97 | _this_->VirtualTablePtr[VirtualFuncNum](_this_, forward(args)...); 98 | } 99 | 100 | int main(int argc, char** argv) 101 | { 102 | srand((unsigned int)time(NULL)); 103 | 104 | cout << "===> Base start <===" << endl; 105 | Base* base = (Base*)operator new(sizeof(Base)); 106 | Base::BaseConstructor(base); 107 | 108 | VirtualDispatch(base, VIRTUAL_FUNCTION_1, rand() % 100); 109 | VirtualDispatch(base, VIRTUAL_FUNCTION_2, rand() % 100); 110 | 111 | Base::BaseDestructor(base); 112 | operator delete(base); 113 | cout << "===> Base end <===" << endl << endl; 114 | 115 | cout << "===> Derived start <===" << endl; 116 | Base* derived = (Base*)operator new(sizeof(Derived)); 117 | Derived::DerivedConstructor(derived); 118 | 119 | VirtualDispatch(derived, VIRTUAL_FUNCTION_1, rand() % 100); 120 | VirtualDispatch(derived, VIRTUAL_FUNCTION_2, rand() % 100); 121 | 122 | Derived::DerivedDestructor(derived); 123 | operator delete(derived); 124 | cout << "===> Derived end <===" << endl << endl; 125 | 126 | return 1; 127 | } 128 | -------------------------------------------------------------------------------- /src/round.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // solution 1.1 11 | // can specify number of digits at run-time as the second parameter 12 | // slowest due to 3 function calls 13 | template 14 | requires std::is_floating_point_v 15 | auto runtime_round1(T v, unsigned d) 16 | { 17 | auto p = std::pow(T(10), T(d)); 18 | if(std::abs(v) > std::numeric_limits::max() / p) 19 | return v; // v * p would overflow, do nothing... 20 | return std::round(v * p) / p; 21 | } 22 | 23 | // solution 1.2 24 | // faster runtime solution, powers of 10 are compiled in, 2 function calls 25 | // approach suggested by Ben from https://thoughts-on-coding.com 26 | template 27 | requires std::is_floating_point_v 28 | auto runtime_round2(T v, unsigned d) 29 | { 30 | static const auto k_p = std::array 31 | { 32 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 33 | 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19 34 | }; 35 | auto p = k_p.at(d); 36 | if(std::abs(v) > std::numeric_limits::max() / p) 37 | return v; // v * p would overflow, do nothing... 38 | return std::round(v * p) / p; 39 | } 40 | 41 | // sloution 2 42 | // if used only with other constexpr the result will be evaluated 43 | // entirely at compile time meaning no runtime cost :) 44 | 45 | // recursive template to compute B^E at compile time 46 | // result is stored as a static variable 'value' of type T 47 | template 48 | requires std::is_arithmetic_v 49 | struct power_of 50 | { 51 | static constexpr T value = T(B) * power_of::value; 52 | }; 53 | 54 | // terminating template for the recursion one above once E == 0 55 | template 56 | requires std::is_arithmetic_v 57 | struct power_of 58 | { 59 | static constexpr T value = T(1); 60 | }; 61 | 62 | template 63 | inline constexpr T power_of_v = power_of::value; 64 | 65 | // recursive function template to calculate b^e 66 | // if both parameters are constexpr it will evaluate at compile time 67 | // otherwise it will evaluate at run time 68 | // returns the result as type T 69 | template 70 | requires std::is_arithmetic_v 71 | constexpr T power_of_f(std::uint64_t b, unsigned e) 72 | { 73 | return e == 0 ? T(1) : T(b) * power_of_f(b, e - 1); 74 | } 75 | 76 | // given a value 'v' return +1 if v is >= 0, otherwise return -1 77 | template 78 | requires std::is_arithmetic_v 79 | constexpr auto my_sign(T v) 80 | { 81 | return v >= T(0) ? T(1) : T(-1); 82 | } 83 | 84 | // given a value 'v' return it's absolute value 85 | template 86 | requires std::is_arithmetic_v 87 | constexpr auto my_abs(T v) 88 | { 89 | return v >= T(0) ? v : -v; 90 | } 91 | 92 | // round float/double/long double value 'v' to the nearest integer 93 | // using compile time type conversions 94 | template 95 | requires std::is_floating_point_v 96 | constexpr auto my_rnd(T v) 97 | { 98 | constexpr auto h = T(0.5) - std::numeric_limits::epsilon(); 99 | return (std::int64_t)(v + h * my_sign(v)); 100 | } 101 | 102 | // self explanatory :) 103 | // though number of digits must be provided at compile time 104 | // as the first template parameter 'D' 105 | template 106 | requires std::is_floating_point_v 107 | constexpr auto constexpr_round(T v) 108 | { 109 | if(D >= std::numeric_limits::digits10) 110 | return v; // 10 ^ D would not fit in int64_t, do nothing 111 | //constexpr auto p = power_of_f(10, D); 112 | constexpr auto p = power_of_v<10, D, T>; 113 | if(my_abs(v) > std::numeric_limits::max() / p) 114 | return v; // v * p would overflow, do nothing 115 | if(my_abs(v) * p > T(std::numeric_limits::max())) 116 | return v; // v * p would not fit in int64_t, do nothing 117 | return my_rnd(v * p) / p; 118 | } 119 | -------------------------------------------------------------------------------- /cmake/FindMYSQL.cmake: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------- 2 | # Copyright (C) 1995-2007 MySQL AB 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of version 2 of the GNU General Public License as 6 | # published by the Free Software Foundation. 7 | # 8 | # There are special exceptions to the terms and conditions of the GPL 9 | # as it is applied to this software. View the full text of the exception 10 | # in file LICENSE.exceptions in the top-level directory of this software 11 | # distribution. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 21 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 22 | # 23 | # The MySQL Connector/ODBC is licensed under the terms of the 24 | # GPL, like most MySQL Connectors. There are special exceptions 25 | # to the terms and conditions of the GPL as it is applied to 26 | # this software, see the FLOSS License Exception available on 27 | # mysql.com. 28 | 29 | ########################################################################## 30 | 31 | 32 | #-------------- FIND MYSQL_INCLUDE_DIR ------------------ 33 | FIND_PATH(MYSQL_INCLUDE_DIR mysql.h 34 | /usr/include/mysql 35 | /usr/local/include/mysql 36 | /opt/mysql/mysql/include 37 | /opt/mysql/mysql/include/mysql 38 | /opt/mysql/include 39 | /opt/local/include/mysql5 40 | /usr/local/mysql/include 41 | /usr/local/mysql/include/mysql 42 | $ENV{ProgramFiles}/MySQL/*/include 43 | $ENV{SystemDrive}/MySQL/*/include) 44 | 45 | #----------------- FIND MYSQL_LIB_DIR ------------------- 46 | IF (WIN32) 47 | # Set lib path suffixes 48 | # dist = for mysql binary distributions 49 | # build = for custom built tree 50 | IF (CMAKE_BUILD_TYPE STREQUAL Debug) 51 | SET(libsuffixDist debug) 52 | SET(libsuffixBuild Debug) 53 | ELSE (CMAKE_BUILD_TYPE STREQUAL Debug) 54 | SET(libsuffixDist opt) 55 | SET(libsuffixBuild Release) 56 | ADD_DEFINITIONS(-DDBUG_OFF) 57 | ENDIF (CMAKE_BUILD_TYPE STREQUAL Debug) 58 | 59 | FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient 60 | PATHS 61 | $ENV{MYSQL_DIR}/lib/${libsuffixDist} 62 | $ENV{MYSQL_DIR}/libmysql 63 | $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} 64 | $ENV{MYSQL_DIR}/client/${libsuffixBuild} 65 | $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} 66 | $ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist} 67 | $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist}) 68 | ELSE (WIN32) 69 | FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient_r mysqlclient 70 | PATHS 71 | /usr/lib/mysql 72 | /usr/local/lib/mysql 73 | /usr/local/mysql/lib 74 | /usr/local/mysql/lib/mysql 75 | /opt/local/mysql5/lib 76 | /opt/local/lib/mysql5/mysql 77 | /opt/mysql/mysql/lib/mysql 78 | /opt/mysql/lib/mysql) 79 | ENDIF (WIN32) 80 | 81 | IF(MYSQL_LIB) 82 | GET_FILENAME_COMPONENT(MYSQL_LIB_DIR ${MYSQL_LIB} PATH) 83 | ENDIF(MYSQL_LIB) 84 | 85 | IF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR) 86 | SET(MYSQL_FOUND TRUE) 87 | 88 | INCLUDE_DIRECTORIES(${MYSQL_INCLUDE_DIR}) 89 | LINK_DIRECTORIES(${MYSQL_LIB_DIR}) 90 | 91 | FIND_LIBRARY(MYSQL_ZLIB zlib PATHS ${MYSQL_LIB_DIR}) 92 | FIND_LIBRARY(MYSQL_TAOCRYPT taocrypt PATHS ${MYSQL_LIB_DIR}) 93 | IF (MYSQL_LIB) 94 | SET(MYSQL_CLIENT_LIBS ${MYSQL_LIB}) 95 | ELSE() 96 | SET(MYSQL_CLIENT_LIBS mysqlclient_r) 97 | ENDIF() 98 | IF (MYSQL_ZLIB) 99 | SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} zlib) 100 | ENDIF (MYSQL_ZLIB) 101 | IF (MYSQL_TAOCRYPT) 102 | SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} taocrypt) 103 | ENDIF (MYSQL_TAOCRYPT) 104 | # Added needed mysqlclient dependencies on Windows 105 | IF (WIN32) 106 | SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ws2_32) 107 | ENDIF (WIN32) 108 | 109 | MESSAGE(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}") 110 | MESSAGE(STATUS "MySQL client libraries: ${MYSQL_CLIENT_LIBS}") 111 | ELSEIF (MySQL_FIND_REQUIRED) 112 | MESSAGE(FATAL_ERROR "Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}") 113 | ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR) 114 | -------------------------------------------------------------------------------- /src/synchronized.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace detail 7 | { 8 | template 9 | class locker 10 | { 11 | public: 12 | using mutex_type = Lock; 13 | 14 | locker(T& v, Lock& l) 15 | : m_value(v), m_guard(l) {} 16 | 17 | locker(const locker&) = delete; 18 | locker& operator = (const locker&) = delete; 19 | 20 | T* operator -> () { return &m_value; } 21 | const T* operator -> () const { return &m_value; } 22 | 23 | private: 24 | T& m_value; 25 | std::scoped_lock m_guard; 26 | }; 27 | 28 | template 29 | class const_locker 30 | { 31 | public: 32 | using mutex_type = Lock; 33 | 34 | const_locker(T& v, Lock& l) 35 | : m_value(v), m_guard(l) {} 36 | 37 | const_locker(const const_locker&) = delete; 38 | const_locker& operator = (const const_locker&) = delete; 39 | 40 | const T* operator -> () const { return &m_value; } 41 | 42 | private: 43 | T& m_value; 44 | std::scoped_lock m_guard; 45 | }; 46 | 47 | 48 | 49 | template 50 | class shared_locker 51 | { 52 | public: 53 | using mutex_type = Lock; 54 | 55 | shared_locker(T& v, Lock& l) 56 | : m_value(v), m_guard(l) {} 57 | 58 | shared_locker(const shared_locker&) = delete; 59 | shared_locker& operator = (const shared_locker&) = delete; 60 | 61 | T* operator -> () { return &m_value; } 62 | const T* operator -> () const { return &m_value; } 63 | 64 | private: 65 | T& m_value; 66 | std::unique_lock m_guard; 67 | }; 68 | 69 | template 70 | class const_shared_locker 71 | { 72 | public: 73 | using mutex_type = Lock; 74 | 75 | const_shared_locker(T& v, Lock& l) 76 | : m_value(v), m_guard(l) {} 77 | 78 | const_shared_locker(const const_shared_locker&) = delete; 79 | const_shared_locker& operator = (const const_shared_locker&) = delete; 80 | 81 | const T* operator -> () const { return &m_value; } 82 | 83 | private: 84 | T& m_value; 85 | std::shared_lock m_guard; 86 | }; 87 | } 88 | 89 | 90 | 91 | template 92 | class synchronized 93 | { 94 | public: 95 | using value_type = T; 96 | using mutex_type = Lock; 97 | 98 | synchronized() = default; 99 | synchronized(const T& v) : m_value(v) {} 100 | synchronized(T&& v) : m_value(std::move(v)) {} 101 | 102 | template>* = nullptr> 104 | synchronized(A&&... a) : m_value(std::forward(a)...) {} 105 | 106 | template>>* = nullptr> 108 | synchronized(std::initializer_list l) : m_value(l) {} 109 | 110 | synchronized(const synchronized&) = delete; 111 | synchronized& operator = (const synchronized&) = delete; 112 | 113 | detail::locker operator -> () 114 | { 115 | return detail::locker(m_value, m_lock); 116 | } 117 | 118 | detail::const_locker operator -> () const 119 | { 120 | return detail::const_locker(m_value, m_lock); 121 | } 122 | 123 | private: 124 | T m_value = T(); 125 | mutable Lock m_lock; 126 | }; 127 | 128 | 129 | 130 | template 131 | class shared_synchronized 132 | { 133 | public: 134 | using value_type = T; 135 | using mutex_type = Lock; 136 | 137 | shared_synchronized() = default; 138 | shared_synchronized(const T& v) : m_value(v) {} 139 | shared_synchronized(T&& v) : m_value(std::move(v)) {} 140 | 141 | template>* = nullptr> 143 | shared_synchronized(A&&... a) : m_value(std::forward(a)...) {} 144 | 145 | template>>* = nullptr> 147 | shared_synchronized(std::initializer_list l) : m_value(l) {} 148 | 149 | shared_synchronized(const shared_synchronized&) = delete; 150 | shared_synchronized& operator = (const shared_synchronized&) = delete; 151 | 152 | detail::shared_locker operator -> () 153 | { 154 | return detail::shared_locker(m_value, m_lock); 155 | } 156 | 157 | detail::const_shared_locker operator -> () const 158 | { 159 | return detail::const_shared_locker(m_value, m_lock); 160 | } 161 | 162 | private: 163 | T m_value = T(); 164 | mutable Lock m_lock; 165 | }; 166 | -------------------------------------------------------------------------------- /src/crypto.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | class RSAKeyPair; 28 | using RSAKeyPairPtr = std::unique_ptr; 29 | 30 | class RSAKeyPair 31 | { 32 | public: 33 | static auto Create(std::size_t bits = 4096) 34 | { 35 | return RSAKeyPair(std::make_unique(kRNG, bits)); 36 | } 37 | 38 | static void Save(const RSAKeyPair& key, const std::string& name, const std::string& pass) 39 | { 40 | auto text = Botan::PKCS8::PEM_encode(*key.m_key.get(), kRNG, pass); 41 | 42 | std::ofstream ofs; 43 | ofs.exceptions(std::ios::failbit | std::ios::badbit); 44 | ofs.open(name, std::ios::out | std::ios::trunc); 45 | ofs.write(text.c_str(), text.length()); 46 | ofs.close(); 47 | } 48 | 49 | static auto CreateAndSave(const std::string& name, const std::string& pass, std::size_t bits = 4096) 50 | { 51 | auto key = Create(bits); 52 | Save(key, name, pass); 53 | return std::move(key); 54 | } 55 | 56 | static auto Load(const std::string& file, const std::string& pass, bool check = true, bool strong = false) 57 | { 58 | auto key = std::unique_ptr(Botan::PKCS8::load_key(file, kRNG, pass)); 59 | if (check && !key->check_key(kRNG, strong)) 60 | throw std::invalid_argument("Invalid key!"); 61 | 62 | return RSAKeyPair(std::move(key)); 63 | } 64 | 65 | auto Encrypt(const std::string& str) const 66 | { 67 | return m_enc->encrypt((const std::uint8_t*)str.data(), str.length(), kRNG); 68 | } 69 | 70 | auto Encrypt(const std::vector& data) const 71 | { 72 | return m_enc->encrypt((const std::uint8_t*)data.data(), data.size(), kRNG); 73 | } 74 | 75 | private: 76 | RSAKeyPair(std::unique_ptr&& key) 77 | : m_key(std::move(key)), 78 | m_enc(std::make_unique(*m_key, kRNG, "EME1(SHA-256)")), 79 | m_dec(std::make_unique(*m_key, kRNG, "EME1(SHA-256)")) {} 80 | 81 | std::unique_ptr m_key; 82 | std::unique_ptr m_enc; 83 | std::unique_ptr m_dec; 84 | 85 | inline static Botan::AutoSeeded_RNG kRNG; 86 | }; 87 | 88 | 89 | 90 | using namespace std; 91 | 92 | string Hash_CryptoPP(const string& msg) 93 | { 94 | CryptoPP::SHA1 hash; 95 | std::string digest(hash.DigestSize(), '*'); 96 | stringstream output; 97 | 98 | hash.Update((const unsigned char*)msg.data(), msg.size()); 99 | hash.Final((unsigned char*)&digest[0]); 100 | 101 | CryptoPP::HexEncoder encoder(new CryptoPP::FileSink(output)); 102 | CryptoPP::StringSource(digest, true, new CryptoPP::Redirector(encoder)); 103 | 104 | return output.str(); 105 | } 106 | 107 | string Hash_WolfSSL(const string& msg) 108 | { 109 | Sha sha; 110 | ::byte shaSum[SHA_DIGEST_SIZE]; 111 | stringstream output; 112 | 113 | wc_InitSha(&sha); 114 | wc_ShaUpdate(&sha, (::byte*)msg.data(), msg.length()); 115 | wc_ShaFinal(&sha, shaSum); 116 | 117 | string digest(shaSum, shaSum + SHA_DIGEST_SIZE); 118 | CryptoPP::HexEncoder encoder(new CryptoPP::FileSink(output)); 119 | CryptoPP::StringSource(digest, true, new CryptoPP::Redirector(encoder)); 120 | 121 | return output.str(); 122 | } 123 | 124 | string Hash_Botan(const string& msg) 125 | { 126 | auto hash = Botan::HashFunction::create("SHA-1"); 127 | hash->update((uint8_t*)msg.data(), msg.length()); 128 | return Botan::hex_encode(hash->final()); 129 | } 130 | 131 | int main() 132 | { 133 | // RSAKeyPair::CreateAndSave("key.txt", "123", 1024); 134 | // auto key = RSAKeyPair::Load("key.txt", "123"); 135 | // return 0; 136 | 137 | std::string msg = "Vorbrodt's C++ Blog @ https://vorbrodt.blog"; 138 | 139 | cout << "Message: " << msg << endl; 140 | cout << "Digest : " << Hash_CryptoPP(msg) << endl << endl; 141 | 142 | cout << "Message: " << msg << endl; 143 | cout << "Digest : " << Hash_WolfSSL(msg) << endl << endl; 144 | 145 | cout << "Message: " << msg << endl; 146 | cout << "Digest : " << Hash_Botan(msg) << endl << endl; 147 | } 148 | -------------------------------------------------------------------------------- /src/istring.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | inline namespace detail 13 | { 14 | template 15 | inline auto char_ieq(CharT c1, CharT c2, const std::locale& loc = std::locale()) 16 | { 17 | return std::toupper(c1, loc) == std::toupper(c2, loc); 18 | }; 19 | 20 | template 21 | inline auto char_ilt(CharT c1, CharT c2, const std::locale& loc = std::locale()) 22 | { 23 | return std::toupper(c1, loc) < std::toupper(c2, loc); 24 | }; 25 | 26 | template 27 | inline auto string_icmp(const CharT* s1, std::size_t n1, const CharT* s2, std::size_t n2, const std::locale& loc = std::locale()) 28 | { 29 | if(std::lexicographical_compare(s1, s1 + n1, s2, s2 + n2, [&](CharT c1, CharT c2) { return char_ilt(c1, c2, loc); })) return -1; 30 | if(std::lexicographical_compare(s2, s2 + n2, s1, s1 + n1, [&](CharT c1, CharT c2) { return char_ilt(c1, c2, loc); })) return 1; 31 | return 0; 32 | } 33 | 34 | template 35 | struct char_itraits : std::char_traits 36 | { 37 | static auto eq(CharT c1, CharT c2) 38 | { 39 | return char_ieq(c1, c2); 40 | } 41 | 42 | static auto lt(CharT c1, CharT c2) 43 | { 44 | return char_ilt(c1, c2); 45 | } 46 | 47 | static auto compare(const CharT* s1, const CharT* s2, std::size_t n) 48 | { 49 | return string_icmp(s1, n, s2, n); 50 | } 51 | }; 52 | } 53 | 54 | template> 55 | class basic_istring : public std::basic_string, Alloc> 56 | { 57 | public: 58 | using base = std::basic_string, Alloc>; 59 | using base::base; 60 | 61 | template, Traits2>, void>* = nullptr> 63 | explicit basic_istring(const std::basic_string& str) 64 | : base(str.data(), str.length()) {} 65 | 66 | explicit operator auto () const 67 | { 68 | return std::basic_string(this->data(), this->length()); 69 | } 70 | 71 | template 72 | std::enable_if_t, Traits2>, bool> 73 | friend operator == (const basic_istring& lhs, std::basic_string& rhs) 74 | { 75 | return string_icmp(lhs.data(), lhs.length(), rhs.data(), rhs.length()) == 0; 76 | } 77 | 78 | template 79 | std::enable_if_t, Traits2>, std::strong_ordering> 80 | friend operator <=> (const basic_istring& lhs, std::basic_string& rhs) 81 | { 82 | return string_icmp(lhs.data(), lhs.length(), rhs.data(), rhs.length()) <=> 0; 83 | } 84 | 85 | template 86 | std::enable_if_t, Traits2>, bool> 87 | friend operator == (std::basic_string& lhs, const basic_istring& rhs) 88 | { 89 | return string_icmp(lhs.data(), lhs.length(), rhs.data(), rhs.length()) == 0; 90 | } 91 | 92 | template 93 | std::enable_if_t, Traits2>, std::strong_ordering> 94 | friend operator <=> (std::basic_string& lhs, const basic_istring& rhs) 95 | { 96 | return string_icmp(lhs.data(), lhs.length(), rhs.data(), rhs.length()) <=> 0; 97 | } 98 | }; 99 | 100 | using istring = basic_istring; 101 | using iwstring = basic_istring; 102 | 103 | inline auto& operator >> (std::istream& is, istring& istr) 104 | { 105 | std::string temp; 106 | is >> temp; 107 | istr = istring(temp.data(), temp.length()); 108 | return is; 109 | } 110 | 111 | inline auto& operator >> (std::wistream& wis, iwstring& iwstr) 112 | { 113 | std::wstring temp; 114 | wis >> temp; 115 | iwstr = iwstring(temp.data(), temp.length()); 116 | return wis; 117 | } 118 | 119 | inline auto& operator << (std::ostream& os, const istring& istr) 120 | { 121 | os << istr.c_str(); 122 | return os; 123 | } 124 | 125 | inline auto& operator << (std::wostream& wos, const iwstring& iwstr) 126 | { 127 | wos << iwstr.c_str(); 128 | return wos; 129 | } 130 | 131 | inline auto operator ""_is(const char* istr, std::size_t len) 132 | { 133 | return istring(istr, len); 134 | } 135 | 136 | inline auto operator ""_is(const wchar_t* iwstr, std::size_t len) 137 | { 138 | return iwstring(iwstr, len); 139 | } 140 | -------------------------------------------------------------------------------- /src/lrpc_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "lsf.hpp" 14 | #include "socket.hpp" 15 | #include "event.hpp" 16 | #include "lrpc_proto.hpp" 17 | 18 | class RPCClient 19 | { 20 | public: 21 | template 22 | RPCClient(const char* host, std::uint16_t port, std::chrono::duration timeout) 23 | : m_socket{ host, port }, m_timeout{ std::chrono::duration_cast(timeout) } 24 | { 25 | m_reply_handlers[cmd_t(CMD::AddRep)] = [this](client_socket&, socket_buffer_t data) 26 | { 27 | auto ut = m_serializer.unpack(data); 28 | auto& packet = std::get<0>(ut); 29 | auto& ctx = m_ctx[packet.hdr.seq]; 30 | 31 | ctx.add_reply = packet; 32 | ctx.reply_event->signal(); 33 | }; 34 | 35 | m_reply_handlers[cmd_t(CMD::SubRep)] = [this](client_socket&, socket_buffer_t data) 36 | { 37 | auto ut = m_serializer.unpack(data); 38 | auto& packet = std::get<0>(ut); 39 | auto& ctx = m_ctx[packet.hdr.seq]; 40 | 41 | ctx.sub_reply = packet; 42 | ctx.reply_event->signal(); 43 | }; 44 | 45 | m_socket.set_data_handler([this](client_socket& self, socket_buffer_t data) 46 | { 47 | auto ut = m_serializer.unpack(data); 48 | auto& header = std::get<0>(ut); 49 | auto& handler = m_reply_handlers.at(header.cmd); 50 | 51 | handler(self, std::move(data)); 52 | }); 53 | } 54 | 55 | int Add(int x, int y) 56 | { 57 | auto sn = m_next_seq_num++; 58 | auto cmd = PAdd{ { cmd_t(CMD::Add), sn }, x, y }; 59 | auto data = m_serializer.pack(cmd); 60 | auto& ctx = m_ctx[sn]; 61 | 62 | m_socket.send(data.data(), data.size()); 63 | auto got_it = ctx.reply_event->wait_for(m_timeout); 64 | auto reply = ctx.add_reply; 65 | m_ctx.erase(sn); 66 | 67 | if(!got_it) 68 | throw std::runtime_error("RPCClient::Add timeout waiting for reply!"); 69 | 70 | return reply.res; 71 | } 72 | 73 | int Sub(int x, int y) 74 | { 75 | auto sn = m_next_seq_num++; 76 | auto cmd = PSub{ { cmd_t(CMD::Sub), sn }, x, y }; 77 | auto data = m_serializer.pack(cmd); 78 | auto& ctx = m_ctx[sn]; 79 | 80 | m_socket.send(data.data(), data.size()); 81 | auto got_it = ctx.reply_event->wait_for(m_timeout); 82 | auto reply = ctx.sub_reply; 83 | m_ctx.erase(sn); 84 | 85 | if(!got_it) 86 | throw std::runtime_error("RPCClient::Sub timeout waiting for reply!"); 87 | 88 | return reply.res; 89 | } 90 | 91 | void Start() { std::thread([this]() { while(m_socket.receive()); }).detach(); } 92 | void Stop() { m_socket.close(); } 93 | 94 | private: 95 | seq_t m_next_seq_num = 1; 96 | client_socket m_socket; 97 | serializer m_serializer; 98 | std::chrono::milliseconds m_timeout; 99 | 100 | using reply_handler_t = std::function; 101 | using reply_handler_map_t = std::map; 102 | reply_handler_map_t m_reply_handlers; 103 | 104 | struct cmd_ctx_t 105 | { 106 | using e_ptr = std::unique_ptr; 107 | e_ptr reply_event = std::make_unique(); 108 | 109 | union 110 | { 111 | PAddRep add_reply; 112 | PSubRep sub_reply; 113 | }; 114 | }; 115 | 116 | using cmd_ctx_map_t = std::map; 117 | cmd_ctx_map_t m_ctx; 118 | }; 119 | 120 | int main(int argc, char** argv) 121 | { 122 | using namespace std; 123 | using namespace std::chrono; 124 | 125 | if(argc != 3) 126 | { 127 | cerr << "USAGE: " << argv[0] << " [host name] [port]" << endl; 128 | return 1; 129 | } 130 | 131 | try 132 | { 133 | srand(time(NULL)); 134 | 135 | auto host = argv[1]; 136 | auto port = stoul(argv[2]); 137 | auto c = RPCClient(host, port, 1s); 138 | 139 | c.Start(); 140 | 141 | for(;;)//int i = 1; i <= 99; ++i) 142 | { 143 | auto num1 = 50 - (rand() % 101); 144 | auto num2 = 50 - (rand() % 101); 145 | auto add = c.Add(num1, num2); 146 | cout << setfill(' ') << setw(3) << num1 << " + " << setw(3) << num2 << " = " << setw(3) << add << std::endl; 147 | this_thread::sleep_for(250ms); 148 | 149 | auto num3 = 50 - (rand() % 101); 150 | auto num4 = 50 - (rand() % 101); 151 | auto sub = c.Sub(num3, num4); 152 | cout << setfill(' ') << setw(3) << num3 << " - " << setw(3) << num4 << " = " << setw(3) << sub << std::endl; 153 | this_thread::sleep_for(250ms); 154 | } 155 | } 156 | catch(exception& ex) 157 | { 158 | cerr << ex.what() << endl; 159 | } 160 | } 161 | --------------------------------------------------------------------------------