Initial commit of a small project to keep a record of various things in C++ that confuses me, other people, or are just down right dumb and should be avoided. This will be where I link people who tell me something is fast or better.

master
Zed A. Shaw 2 weeks ago
parent 57b4dd6b61
commit bba4b2463e
  1. 10
      .gdbinit
  2. 29
      .gitignore
  3. 1
      .vimrc_proj
  4. 29
      Makefile
  5. 61
      TracyClient.cpp
  6. 44
      dbc.cpp
  7. 29
      dbc.hpp
  8. 100
      emplace_move/test.cpp
  9. 51
      meson.build
  10. 1
      meson.options
  11. 156
      rvo/test.cpp
  12. 7
      scripts/coverage_reset.ps1
  13. 11
      scripts/coverage_reset.sh
  14. 7
      scripts/reset_build.ps1
  15. 11
      scripts/reset_build.sh
  16. 10
      stats.cpp
  17. 45
      stats.hpp
  18. 9
      tests/base.cpp
  19. 11
      wraps/catch2.wrap
  20. 13
      wraps/fmt.wrap
  21. 11
      wraps/nlohmann_json.wrap
  22. 7
      wraps/tracy.wrap

@ -0,0 +1,10 @@
set confirm off
set breakpoint pending on
set logging on
set logging overwrite on
set print pretty on
set pagination off
break abort
#break _invalid_parameter_noinfo
#break _invalid_parameter
catch throw

29
.gitignore vendored

@ -0,0 +1,29 @@
# ---> Vim
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
subprojects
builddir
ttassets
backup
*.exe
*.dll
*.world
coverage

@ -0,0 +1 @@
set makeprg=meson\ compile\ -C\ .

@ -0,0 +1,29 @@
all: build test
reset:
powershell -executionpolicy bypass .\scripts\reset_build.ps1
%.cpp : %.rl
ragel -o $@ $<
build:
meson compile -j 10 -C builddir
release_build:
meson --wipe builddir -Db_ndebug=true --buildtype release
meson compile -j 10 -C builddir
debug_build:
meson setup --wipe builddir --buildtype debug
meson compile -j 10 -C builddir
tracy_build:
meson setup --wipe builddir --buildtype debugoptimized -Dtracy_enable=true -Dtracy:on_demand=true
meson compile -j 10 -C builddir
test: build
./builddir/emplace_test.exe
./builddir/rvo_test.exe
clean:
meson compile --clean -C builddir

@ -0,0 +1,61 @@
//
// Tracy profiler
// ----------------
//
// For fast integration, compile and
// link with this source file (and none
// other) in your executable (or in the
// main DLL / shared object on multi-DLL
// projects).
//
// Define TRACY_ENABLE to enable profiler.
#include "common/TracySystem.cpp"
#ifdef TRACY_ENABLE
#ifdef _MSC_VER
# pragma warning(push, 0)
#endif
#include "common/tracy_lz4.cpp"
#include "client/TracyProfiler.cpp"
#include "client/TracyCallstack.cpp"
#include "client/TracySysPower.cpp"
#include "client/TracySysTime.cpp"
#include "client/TracySysTrace.cpp"
#include "common/TracySocket.cpp"
#include "client/tracy_rpmalloc.cpp"
#include "client/TracyDxt1.cpp"
#include "client/TracyAlloc.cpp"
#include "client/TracyOverride.cpp"
#include "client/TracyKCore.cpp"
#if defined(TRACY_HAS_CALLSTACK)
# if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
# include "libbacktrace/alloc.cpp"
# include "libbacktrace/dwarf.cpp"
# include "libbacktrace/fileline.cpp"
# include "libbacktrace/mmapio.cpp"
# include "libbacktrace/posix.cpp"
# include "libbacktrace/sort.cpp"
# include "libbacktrace/state.cpp"
# if TRACY_HAS_CALLSTACK == 4
# include "libbacktrace/macho.cpp"
# else
# include "libbacktrace/elf.cpp"
# endif
# include "common/TracyStackFrames.cpp"
# endif
#endif
#ifdef _MSC_VER
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "dbghelp.lib")
# pragma comment(lib, "advapi32.lib")
# pragma comment(lib, "user32.lib")
# pragma warning(pop)
#endif
#endif

@ -0,0 +1,44 @@
#include "dbc.hpp"
#include <iostream>
void dbc::log(const string &message) {
std::cerr << "!!!!!!!!!!" << message << std::endl;
}
void dbc::sentinel(const string &message) {
string err = fmt::format("[SENTINEL!] {}", message);
dbc::log(err);
throw dbc::SentinelError{err};
}
void dbc::pre(const string &message, bool test) {
if(!test) {
string err = fmt::format("[PRE!] {}", message);
dbc::log(err);
throw dbc::PreCondError{err};
}
}
void dbc::pre(const string &message, std::function<bool()> tester) {
dbc::pre(message, tester());
}
void dbc::post(const string &message, bool test) {
if(!test) {
string err = fmt::format("[POST!] {}", message);
dbc::log(err);
throw dbc::PostCondError{err};
}
}
void dbc::post(const string &message, std::function<bool()> tester) {
dbc::post(message, tester());
}
void dbc::check(bool test, const string &message) {
if(!test) {
string err = fmt::format("[CHECK!] {}\n", message);
dbc::log(err);
throw dbc::CheckError{err};
}
}

@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <fmt/core.h>
#include <functional>
using std::string;
namespace dbc {
class Error {
public:
const string message;
Error(string m) : message{m} {}
Error(const char *m) : message{m} {}
};
class CheckError : public Error {};
class SentinelError : public Error {};
class PreCondError : public Error {};
class PostCondError : public Error {};
void log(const string &message);
void sentinel(const string &message);
void pre(const string &message, bool test);
void pre(const string &message, std::function<bool()> tester);
void post(const string &message, bool test);
void post(const string &message, std::function<bool()> tester);
void check(bool test, const string &message);
}

@ -0,0 +1,100 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace fmt;
// Some heavy object
struct A {
A() = default;
A(int val) : val(val) {
std::cout << "called constructor with: " << val << std::endl;
}
A(A const& other) {
std::cout << "calling copy(A const& other): " << val << std::endl;;
}
A(A&& other) {
std::cout << "calling move(A&& other): " << val << std::endl;;
}
A& operator=(A&& other) {
std::cout << "calling move(A&& other)=: " << val << std::endl;;
return *this;
}
A& operator=(A const& other) {
std::cout << "calling copy(A const& other)=: " << val << std::endl;;
return *this;
}
~A() = default;
int val{};
};
TEST_CASE("emplace move tests", "[emplace]") {
A a{10};
std::vector<A> test;
test.reserve(10); // to prevent vector resizing and creating new objects on the way
std::cout << "===== Emplacing Start =====\n";
// pass by l-value and will call copy constructor since it'll match
// A(A const&)
std::cout << "== 1. Emplace: Calling copy constructor \n";
test.emplace_back(a);
// pass by r-value and will call move constructor since it'll match
// A(A&&)
std::cout << "== 2. Emplace: Calling move constructor \n";
test.emplace_back(std::move(a));
std::cout << "== 3. Emplace: Calling move constructor without direct constructor\n";
test.emplace_back(100);
// pass by pr-value (pure r-value which is those value that has not come to an existance yet)
// and will call move constructor since it'll match A(A&&).
// "copy-elision" could be applied here but I don't know why compilers
// refused to do it. Maybe how vectors are implemented and you need
// to put the value in a slot so the compiler had to call the move constructor
std::cout << "== 4. Emplace: Calling move constructor\n";
test.emplace_back(A{});
}
TEST_CASE("pushback move tests", "[push_back]") {
std::cout << "\n\n!!!!!!!!!! PUSH BACK alternative !!!!!!!!!!\n";
A a{20};
std::vector<A> test;
test.reserve(10); // to prevent vector resizing and creating new objects on the way
std::cout << "===== Push Back Start =====\n";
// pass by l-value and will call copy constructor since it'll match
// A(A const&)
std::cout << "== 1. Push Back: Calling copy constructor \n";
test.push_back(a);
// pass by r-value and will call move constructor since it'll match
// A(A&&)
std::cout << "== 2. Push Back: Calling move constructor \n";
test.push_back(std::move(a));
std::cout << "== 3. Push Back: Calling move constructor without direct constructor\n";
test.push_back(200);
// pass by pr-value (pure r-value which is those value that has not come to an existance yet)
// and will call move constructor since it'll match A(A&&).
// "copy-elision" could be applied here but I don't know why compilers
// refused to do it. Maybe how vectors are implemented and you need
// to put the value in a slot so the compiler had to call the move constructor
std::cout << "== 4. Push Back: Calling move constructor\n";
test.push_back(A{});
}
TEST_CASE("proof you don't need emplace or std::move", "[proof]") {
}

@ -0,0 +1,51 @@
project('c-plus-plus-dafuq', 'cpp',
version: '0.1.0',
default_options: [
'cpp_std=c++20',
'cpp_args=-DTRACY_ENABLE=1 -D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1',
'cpp_args=-DTRACY_ENABLE=1',
])
exe_defaults = []
cc = meson.get_compiler('cpp')
tracy = dependency('tracy', static: true)
catch2 = dependency('catch2-with-main')
fmt = dependency('fmt', allow_fallback: true)
json = dependency('nlohmann_json')
# not sure if tracy needs these
opengl32 = cc.find_library('opengl32', required: true)
winmm = cc.find_library('winmm', required: true)
gdi32 = cc.find_library('gdi32', required: true)
# i know for sure tracy needs these
ws2_32 = cc.find_library('ws2_32', required: true)
dbghelp = cc.find_library('dbghelp', required: true)
if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized'
warning('Profiling builds should set --buildtype=debugoptimized')
endif
dependencies = [
tracy, catch2, fmt, json, opengl32,
ws2_32, dbghelp
]
# use this for common options only for our executables
cpp_args=[ ]
executable('emplace_test', [
'TracyClient.cpp',
'emplace_move/test.cpp'
],
override_options: exe_defaults,
dependencies: dependencies)
executable('rvo_test', [
'TracyClient.cpp',
'rvo/test.cpp'
],
override_options: exe_defaults,
dependencies: dependencies)

@ -0,0 +1 @@
option('tracy_enable', type: 'boolean', value: false, description: 'Enable profiling')

@ -0,0 +1,156 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace fmt;
// Some heavy object
struct A {
A() = default;
A(int val) : val(val) {
std::cout << "called constructor with: " << val << std::endl;
}
A(A const& other) {
std::cout << "calling copy(A const& other): " << val << std::endl;;
}
A(A&& other) {
std::cout << "calling move(A&& other): " << val << std::endl;;
}
A& operator=(A&& other) {
std::cout << "calling move(A&& other)=: " << val << std::endl;;
return *this;
}
A& operator=(A const& other) {
std::cout << "calling copy(A const& other)=: " << val << std::endl;;
return *this;
}
~A() = default;
int val{};
};
template <typename T>
void print_helper(std::string_view var, T&&) {
std::string name = typeid(T).name();
if constexpr (std::is_const_v<T>) {
name += " const";
}
if constexpr (std::is_lvalue_reference_v<T>) {
name += "&";
} else {
name += "&&";
}
std::cout << "Type of '" << var << "': '" << name <<"'\n";
}
#define print_t(VAR) print_helper(#VAR, VAR)
A rvo_impl_1() {
A a;
std::cout << "rvo_impl_1\n";
a.val = rand() % 2;
return a;
}
// calls the move constructor or it might optimize away it in -O3
std::string rvo_impl_2(int v) {
std::string res;
std::cout << "rvo_impl_2\n";
res = std::to_string(v);
return res;
}
// It'll call copy constructors
std::string rvo_not_work_complex(int v) {
if (v % 5 == 0) return "multiple of five";
if (v % 2 == 0) return "multiple of 2";
return std::to_string(v);
}
// call move constructor
A rvo_not_work_impl(int v) {
std::cout << "rvo_not_work_impl: ";
A a;
if (v % 2) {
a.val = v % 2;
} else {
// returning early
// you might think who would do that let me show you an example
return {};
}
return a;
}
// copy-elision/RVO does not call any constructor since they get optimized away
A copy_elision() {
std::cout << "copy_elision\n";
return A(); // pr-value
}
// RVO(return value optimization) is a part of copy-elision, but "copy-elision" is
// a much wider concept that applies to other parts of the program. It removes
// copy/move constuctors
// What is l-value?
// l-values are references to the objects. These are objects you can hold onto
// like constructed objects, variables or references.
// example:
// 1. int&
// 2. int const&
// 3. int a = 4; foo(a <-- this will be a l-value)
// What is r-value?
// r-values are the values that is temporary.
// example:
// 1. literals like "hello", 1, 2.5
// 2. call to object constructor like A(), std::string("Hello"), ...
// 3. A&&, int&&, ... these are r-values that you've seen inside the move constructors
// There are more value that you can look into "pr-value", ...
// https://en.cppreference.com/w/cpp/language/value_category
// You may be thinking why i'm talking about them. The reason being these concept
// allows you understand when to use "std::move" and when it's useless. For that
// let me implement my version of move constructor
template <typename T>
// decltype(auto) means forward the return type or whatever I'm returning infer the type with
// "const&", "&" or "&&"
decltype(auto) my_move(T&& val) {
// here T&& is not a r-value. It's perfect forwarding. Whenever you see "&&" you see
// with template it means perfect forwading. I know cpp has fucked-up syntax.
using type = std::decay_t<T>;
// all this does is cast l-value to r-value
return static_cast<type&&>(val);
}
TEST_CASE("RVO tests", "[base]") {
A a{10};
int argc;
std::cout << "\n\n######### RVO TESTS ###############\n";
auto t0 = rvo_impl_1(); // no constructor will be called because of return value optimization
auto t1 = rvo_not_work_impl(argc);
auto t2 = copy_elision();
auto t3 = rvo_not_work_complex(argc);
auto t4 = rvo_impl_2(argc);
print_t(a); // pass by l-value | output: Type of 'a': 'A&'
print_t(std::move(a)); // pass by r-value | output: Type of 'std::move(a)': 'A&&'
print_t(my_move(a)); // pass by r-value | output: Type of 'my_move(a)': 'A&&'
print_t(A{}); // pass by r-value | output: Type of 'A{}': 'A&&'
static_assert(!std::is_rvalue_reference_v<decltype(a)>, "'a' is l-value");
// you can remove the negation and see it failing
static_assert(!std::is_lvalue_reference_v<decltype(my_move(a))>, "This should pass if I've implement it correctly");
}

@ -0,0 +1,7 @@
mv .\subprojects\packagecache .
rm -recurse -force .\subprojects\,.\builddir\
mkdir subprojects
mv .\packagecache .\subprojects\
mkdir builddir
cp wraps\*.wrap subprojects\
meson setup --default-library=static --prefer-static -Db_coverage=true builddir

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
mv -f ./subprojects/packagecache .
rm -rf subprojects builddir
mkdir subprojects
mv packagecache ./subprojects/
mkdir builddir
cp wraps/*.wrap subprojects/
# on OSX you can't do this with static
meson setup -Db_coverage=true builddir

@ -0,0 +1,7 @@
mv .\subprojects\packagecache .
rm -recurse -force .\subprojects\,.\builddir\
mkdir subprojects
mv .\packagecache .\subprojects\
mkdir builddir
cp wraps\*.wrap subprojects\
meson setup --default-library=static --prefer-static builddir

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
mv -f ./subprojects/packagecache .
rm -rf subprojects builddir
mkdir subprojects
mv packagecache ./subprojects/
mkdir builddir
cp wraps/*.wrap subprojects/
# on OSX you can't do this with static
meson setup builddir

@ -0,0 +1,10 @@
#include "stats.hpp"
#include <fmt/core.h>
void Stats::dump()
{
fmt::println("sum: {}, sumsq: {}, n: {}, "
"min: {}, max: {}, mean: {}, stddev: {}",
sum, sumsq, n, min, max, mean(),
stddev());
}

@ -0,0 +1,45 @@
#pragma once
#include <cmath>
struct Stats {
double sum = 0.0;
double sumsq = 0.0;
unsigned long n = 0L;
double min = 0.0;
double max = 0.0;
inline void reset() {
sum = 0;
sumsq = 0;
n = 0L;
min = 0;
max = 0;
}
inline double mean() {
return sum / n;
}
inline double stddev() {
return std::sqrt((sumsq - (sum * sum / n)) /
(n - 1));
}
inline void sample(double s) {
sum += s;
sumsq += s * s;
if (n == 0) {
min = s;
max = s;
} else {
if (min > s) min = s;
if (max < s) max = s;
}
n += 1;
}
void dump();
};

@ -0,0 +1,9 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
using namespace fmt;
TEST_CASE("base test", "[base]") {
REQUIRE(1 == 1);
}

@ -0,0 +1,11 @@
[wrap-file]
directory = Catch2-3.7.1
source_url = https://github.com/catchorg/Catch2/archive/v3.7.1.tar.gz
source_filename = Catch2-3.7.1.tar.gz
source_hash = c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/catch2_3.7.1-1/Catch2-3.7.1.tar.gz
wrapdb_version = 3.7.1-1
[provide]
catch2 = catch2_dep
catch2-with-main = catch2_with_main_dep

@ -0,0 +1,13 @@
[wrap-file]
directory = fmt-11.0.2
source_url = https://github.com/fmtlib/fmt/archive/11.0.2.tar.gz
source_filename = fmt-11.0.2.tar.gz
source_hash = 6cb1e6d37bdcb756dbbe59be438790db409cdb4868c66e888d5df9f13f7c027f
patch_filename = fmt_11.0.2-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_11.0.2-1/get_patch
patch_hash = 90c9e3b8e8f29713d40ca949f6f93ad115d78d7fb921064112bc6179e6427c5e
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_11.0.2-1/fmt-11.0.2.tar.gz
wrapdb_version = 11.0.2-1
[provide]
fmt = fmt_dep

@ -0,0 +1,11 @@
[wrap-file]
directory = nlohmann_json-3.11.3
lead_directory_missing = true
source_url = https://github.com/nlohmann/json/releases/download/v3.11.3/include.zip
source_filename = nlohmann_json-3.11.3.zip
source_hash = a22461d13119ac5c78f205d3df1db13403e58ce1bb1794edc9313677313f4a9d
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/nlohmann_json_3.11.3-1/nlohmann_json-3.11.3.zip
wrapdb_version = 3.11.3-1
[provide]
nlohmann_json = nlohmann_json_dep

@ -0,0 +1,7 @@
[wrap-git]
url=https://github.com/wolfpld/tracy.git
revision=v0.11.1
depth=1
[provide]
tracy = tracy_dep
Loading…
Cancel
Save