From a91befb3d616c9daed8315e1bfdf03502452be51 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Wed, 17 Jun 2015 11:59:28 +0200 Subject: [PATCH] Split cpp a lot --- Makefile_many | 39 +- c/enum.c | 14 + c/posix/Makefile | 1 + c/posix/README.md | 3 + c/posix/atomic.c | 78 + c/preprocessor.c | 8 + cpp/Makefile_params | 2 +- cpp/README.md | 36 +- cpp/algorithm.cpp | 356 ++++ cpp/atomic.cpp | 17 +- cpp/atomic_bool.cpp.off | 24 + cpp/attributes.cpp | 32 + cpp/common.hpp | 14 +- cpp/containers.md | 18 + cpp/deque.cpp | 23 + cpp/function.cpp | 15 +- cpp/functional.cpp | 66 + cpp/interactive/Makefile | 1 + cpp/interactive/common.hpp | 1 + cpp/interactive/sleep_for.cpp | 14 + cpp/iterator.cpp | 361 ++++ cpp/list.cpp | 47 + cpp/main.cpp | 2580 +---------------------------- cpp/map.cpp | 189 +++ cpp/mutex.cpp | 14 +- cpp/operator_overload.cpp | 51 +- cpp/preprocessor.cpp | 8 +- cpp/regex.cpp | 18 + cpp/rtti.md | 12 + cpp/set.cpp | 151 ++ cpp/standard_library.md | 30 + cpp/static_assert.cpp | 28 + cpp/string.cpp | 310 ++++ cpp/struct.cpp | 42 + cpp/thread.cpp | 82 +- cpp/typeid.cpp | 69 + cpp/utility.cpp | 177 ++ cpp/valarray.cpp | 61 + cpp/vector.cpp | 528 ++++++ gcc/Makefile_params | 1 - gcc/atomic_fetch_add.c | 33 + posix/Makefile_params | 2 - posix/interactive/Makefile_params | 1 - posix/pthread_mutex.c | 10 +- 44 files changed, 2873 insertions(+), 2694 deletions(-) create mode 120000 c/posix/Makefile create mode 100644 c/posix/README.md create mode 100644 c/posix/atomic.c create mode 100644 cpp/algorithm.cpp create mode 100644 cpp/atomic_bool.cpp.off create mode 100644 cpp/attributes.cpp create mode 100644 cpp/containers.md create mode 100644 cpp/deque.cpp create mode 100644 cpp/functional.cpp create mode 120000 cpp/interactive/Makefile create mode 120000 cpp/interactive/common.hpp create mode 100644 cpp/interactive/sleep_for.cpp create mode 100644 cpp/iterator.cpp create mode 100644 cpp/list.cpp create mode 100644 cpp/map.cpp create mode 100644 cpp/regex.cpp create mode 100644 cpp/rtti.md create mode 100644 cpp/set.cpp create mode 100644 cpp/static_assert.cpp create mode 100644 cpp/string.cpp create mode 100644 cpp/struct.cpp create mode 100644 cpp/typeid.cpp create mode 100644 cpp/utility.cpp create mode 100644 cpp/valarray.cpp create mode 100644 cpp/vector.cpp create mode 100644 gcc/atomic_fetch_add.c delete mode 100644 posix/Makefile_params delete mode 120000 posix/interactive/Makefile_params diff --git a/Makefile_many b/Makefile_many index 40c1ca4..c3b1774 100644 --- a/Makefile_many +++ b/Makefile_many @@ -9,14 +9,15 @@ MYCC ?= gcc MYCXX ?= g++ D ?= #-D_XOPEN_SOURCE 700 +G ?= gdb3 I ?= #-I/usr/include O ?= 0 STD ?= c11 PEDANTIC ?= -pedantic-errors -CFLAGS ?= $(D) -O$(O) -std=$(STD) -Wextra $(PEDANTIC) $(CFLAGS_EXTRA) -MYCXXFLAGS ?= $(D) -O$(O) -std=c++11 -Wextra $(PEDANTIC) $(CXXFLAGS_EXTRA) +CFLAGS ?= $(D) -g$(G) -O$(O) -std=$(STD) -Wextra $(PEDANTIC) $(CFLAGS_EXTRA) +MYCXXFLAGS ?= $(D) -g$(G) -O$(O) -std=c++11 -Wextra $(PEDANTIC) $(CXXFLAGS_EXTRA) -LIBS ?= -lm #-lGL -lGLU -lglut +LIBS ?= -lm -pthread #-lGL -lGLU -lglut # Fortran FF ?= gfortran @@ -40,10 +41,6 @@ RUN_INPUT ?= input TEST ?= test ASSEMBLER_NOEXT ?= $(IN_DIR)$(RUN) -DEBUG_DEFINE ?= -DEBUG_FLAGS ?= -PROFILE_DEFINE ?= #-DDEBUG -PROFILE_FLAGS ?= # RUN_BNAME := $(RUN)$(OUT_EXT) @@ -53,7 +50,7 @@ OUTS_NODIR := $(basename $(INS_NODIR)) OUTS_NODIR := $(addsuffix $(OUT_EXT), $(OUTS_NODIR)) OUTS := $(addprefix $(OUT_DIR), $(OUTS_NODIR)) -.PHONY: all asm set_asm_flags clean debug set_debug_flags help mkdir objdump set_objdump_flags profile set_profile_flags test $(PHONY) +.PHONY: all asm clean debug help mkdir objdump set_objdump_flags profile set_profile_flags test $(PHONY) all: mkdir $(OUTS) @#TODO ignore errors if not present @@ -65,26 +62,26 @@ ifneq ($(strip $(run)),) endif $(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.c - $(MYCC) $(CFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) $(I) -o "$@" "$<" $(LIBS) + $(MYCC) $(CFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(I) -o "$@" "$<" $(LIBS) $(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.cpp - $(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) $(I) -o "$@" "$<" $(LIBS) + $(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(I) -o "$@" "$<" $(LIBS) $(OUT_DIR)%$(OUT_EXT): $(IN_DIR)%.f - $(FF) $(FFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) -o "$@" "$<" $(FFLIBS) + $(FF) $(FFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) -o "$@" "$<" $(FFLIBS) # Make assembly intermingled with original C code to stdout> # TODO0: how not to rewrite the make rules? -# For bare asm: $(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) $(CFLAGS) -fverbose-asm -S "$(ASSEMBLER_NOEXT)$$EXT" +# For bare asm: $(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(CFLAGS) -fverbose-asm -S "$(ASSEMBLER_NOEXT)$$EXT" asm: mkdir set_asm_flags for EXT in $(IN_EXTS); do \ if [ -f "$(ASSEMBLER_NOEXT)$$EXT" ]; then \ case "$$EXT" in \ .c)\ - $(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) $(CFLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS) -o $(OUT_DIR)asm.o\ + $(MYCC) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(CFLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS) -o $(OUT_DIR)asm.o\ ;;\ .cpp)\ - $(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) $(DEBUG_DEFINE) $(DEBUG_FLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS)\ + $(MYCXX) $(MYCXXFLAGS) $(PROFILE_DEFINE) $(PROFILE_FLAGS) -c -fverbose-asm -Wa,-adhln "$(ASSEMBLER_NOEXT)$$EXT" $(LIBS)\ ;;\ .f)\ ;;\ @@ -93,9 +90,6 @@ asm: mkdir set_asm_flags fi;\ done -set_asm_flags: - $(eval DEBUG_FLAGS := -ggdb3) - clean: if [ ! '$(OUT_DIR)' = './' ]; then \ rm -rf '$(OUT_DIR)' ;\ @@ -106,21 +100,14 @@ clean: debug: clean set_debug_flags all cd $(OUT_DIR) && gdb "$(RUN_BNAME)" -set_debug_flags: - $(eval DEBUG_FLAGS := -ggdb3) - $(eval DEBUG_DEFINE := -DDEBUG) - mkdir: @mkdir -p "$(OUT_DIR)" objdump: mkdir set_objdump_flags all - cd $(OUT_DIR) && objdump -S $(RUN_BNAME) - -set_objdump_flags: - $(eval DEBUG_FLAGS := -ggdb3) + cd '$(OUT_DIR)' && objdump -CSr '$(RUN_BNAME)' profile: clean set_profile_flags all run - cd $(OUT_DIR) && gprof -b $(RUN_BNAME) gmon.out | tee "$(RUN_BNAME).profile_out" | less + cd '$(OUT_DIR)' && gprof -b '$(RUN_BNAME)' gmon.out | tee "$(RUN_BNAME).profile_out" | less run: all @echo diff --git a/c/enum.c b/c/enum.c index 07a778d..fd244f9 100644 --- a/c/enum.c +++ b/c/enum.c @@ -41,6 +41,7 @@ int main() { /*, E5*/ }; + /* Enum types can then be cast to int. */ /* If unspecified, the first is 0. */ assert(E0 == 0); assert(E1 == 1); @@ -48,6 +49,19 @@ int main() { /* Continue from the last one. */ assert(E3 == 4); assert(E4 == INT_MAX); + + // int can also be cast to enum? + { + { + enum E e = 0; + assert(e == E0); + } + + { + enum E e = -1; + assert(e == -1); + } + } } /* diff --git a/c/posix/Makefile b/c/posix/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/c/posix/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/c/posix/README.md b/c/posix/README.md new file mode 100644 index 0000000..e78bec0 --- /dev/null +++ b/c/posix/README.md @@ -0,0 +1,3 @@ +# POSIX + +C features which require POSIX to demonstrate, possibly because some parts haven't been implemented yet, mainly multi-threading for now. diff --git a/c/posix/atomic.c b/c/posix/atomic.c new file mode 100644 index 0000000..44ac631 --- /dev/null +++ b/c/posix/atomic.c @@ -0,0 +1,78 @@ +/* +Optional language feature, checked with `__STDC_NO_ATOMICS__`. + +Modeled on C++ `std::atomic` and analogous to it, just without using C++ language features. + +# _Atomic + + - http://stackoverflow.com/questions/24557728/does-any-c-library-implement-c11-threads-for-gnu-linux + +# ATOMIC_VAR_INIT + + Required to initialize auto `_Atomic`, but not globals + + - http://stackoverflow.com/questions/15317666/how-to-initialize-and-use-a-c11-atomic-variable +*/ + +#ifndef __STDC_NO_ATOMICS__ +#include +#include +/* Not in GCC 5.1 yet. */ +/*#include */ +#include + +enum CONSTANTS { + NUM_THREADS = 1000, + NUM_ITERS = 1000 +}; + +_Atomic int global = 0; + +void* main_thread(void *arg) { + int i; + for (i = 0; i < NUM_ITERS; ++i) { + /* + Postrix ++ and compound assignment are atomic. TODO Other operators? + + Other operations need `stdatomic` functions. + + 6.5.2.4/2 "Postfix increment and decrement operators" + + > Postfix ++ on an object with atomic type is a read-modify-write operation + with memory_order_seq_cst memory order semantics. + + 6.5.16.2/3 "Compound assignment": + + > If E1 has an atomic type, compound assignment is a read-modify-write operation + with memory_order_seq_cst memory order semantics. + */ + global++; + } + return NULL; +} +#endif + +int main() { +#ifndef __STDC_NO_ATOMICS__ + int i; + + pthread_t threads[NUM_THREADS]; + for (i = 0; i < NUM_THREADS; ++i) + pthread_create(&threads[i], NULL, main_thread, NULL); + for (i = 0; i < NUM_THREADS; ++i) + pthread_join(threads[i], NULL); + + /* Not in GCC 5.1 yet. */ + /* + thrd_t thr[NUM_THREADS]; + for(int i = 0; n < NUM_THREADS; ++n) + thrd_create(&thr[i], main_thread, NULL); + for(int n = 0; n < NUM_THREADS; ++n) + thrd_join(thr[i], NULL); + */ + + /* If we still had many threads here, I think we would need to use `atomic_load`. */ + assert(global == NUM_THREADS * NUM_ITERS); +#endif + return EXIT_SUCCESS; +} diff --git a/c/preprocessor.c b/c/preprocessor.c index 492fed1..73c415b 100644 --- a/c/preprocessor.c +++ b/c/preprocessor.c @@ -568,6 +568,14 @@ assert(false); assert(sizeof(double) == 8); #endif } + +#ifndef __STDC_NO_ATOMICS__ + /* Indicates no C11 support for `_Atomic` and ``. */ + puts("__STDC_NO_ATOMICS__"); +#else +#include + _Atomic int i; +#endif } return EXIT_SUCCESS; diff --git a/cpp/Makefile_params b/cpp/Makefile_params index 34c043f..976250b 100644 --- a/cpp/Makefile_params +++ b/cpp/Makefile_params @@ -1 +1 @@ -CXXFLAGS_EXTRA := -Wno-sequence-point -Wno-unused-variable -Wno-unused-but-set-variable -pthread +CXXFLAGS_EXTRA := -Wno-sequence-point -Wno-unused-variable -Wno-unused-but-set-variable -pthread diff --git a/cpp/README.md b/cpp/README.md index 982797e..7047384 100644 --- a/cpp/README.md +++ b/cpp/README.md @@ -8,12 +8,17 @@ 1. [main.cpp](main.cpp) 1. [Hello world](hello_world.cpp) 1. [const](const.cpp) - 1. [constexpr](constexpr.cpp) - 1. [auto](auto.cpp) - 1. [decltype](decltype.cpp) + 1. [RTTI](rtti.md) + 1. Compile time magic + 1. [constexpr](constexpr.cpp) + 1. [static_assert](static_assert.cpp) + 1. [typeid](typeid.cpp) + 1. [auto](auto.cpp) + 1. [decltype](decltype.cpp) 1. [nullptr](nullptr.cpp) - 1. [reference](reference.cpp) - 1. [rvalue reference](rvalue_reference.cpp) + 1. References + 1. [reference](reference.cpp) + 1. [rvalue reference](rvalue_reference.cpp) 1. [Function](function.cpp) 1. [Operator overload](operator_overload.cpp) 1. [Conversion function](conversion_function.cpp) @@ -29,10 +34,25 @@ 1. [Preprocessor](preprocessor.cpp) 1. [Standard library](standard_library.md) 1. [Headers](common.hpp) - 1. [limits](limits.cpp) + 1. [Containers](containers.md) + 1. [vector](vector.cpp) + 1. [list](list.cpp) + 1. [map](map.cpp) + 1. [set](set.cpp) + 1. [valarray](valarray.cpp) + 1. [deque](deque.cpp) + 1. [string](string.cpp) 1. [thread](thread.cpp) - 1. [atomic](atomic.cpp) - 1. [mutex](mutex.cpp) + 1. [sleep_for](sleep_for.cpp) + 1. [atomic](atomic.cpp) + 1. [atomic](atomic_bool.cpp.off) + 1. [mutex](mutex.cpp) + 1. [algorithm](algorithm.cpp) + 1. [functional](functional.cpp) + 1. [iterator](iterator.cpp) + 1. [limits](limits.cpp) 1. [memory](memory.cpp) + 1. [regex](regex.cpp) + 1. [utility](utility.cpp) 1. Applications 1. [Design patterns](design_patterns.cpp) diff --git a/cpp/algorithm.cpp b/cpp/algorithm.cpp new file mode 100644 index 0000000..b4a94b4 --- /dev/null +++ b/cpp/algorithm.cpp @@ -0,0 +1,356 @@ +/* +# algorithm +*/ + +#include "common.hpp" + +int main() { + { + assert(std::min(0.1, 0.2) == 0.1); + assert(std::max(0.1, 0.2) == 0.2); + } + + // # sort + { + std::vector v{2, 0, 1}; + std::sort(v.begin(), v.end()); + std::vector v1 = {0, 1, 2}; + assert((v == std::vector{0, 1, 2})); + } + + // # reverse + { + std::vector v{2, 0, 1}; + std::reverse(v.begin(), v.end()); + assert((v == std::vector{1, 0, 2})); + } + + /* + # swap + + Does things equivalent to: + + template void swap (T& a, T& b) + { + T c(a); a=b; b=c; + } + + However stdlib can specialize it to do operations more efficiently. + + Some stdlib classes implement swap as a method. + + Particularly important because of the copy and swap idiom. + */ + + // # randomize + { + std::vector v{2, 0, 1}; + std::random_shuffle(v.begin(), v.end()); + } + + // # copy + { + std::vector v{2, 0, 1}; + std::vector v2(5, 3); + std::copy(v.begin(), v.end(), v2.begin() + 1); + assert(v2 == std::vector({3, 2, 0, 1, 3})); + } + + /* + # equal + + Compares ranges of two containers. + */ + { + std::vector v {0, 1, 2 }; + std::vector v2{ 1, 2, 3}; + assert(std::equal(v.begin() + 1, v.end(), v2.begin())); + } + + /* + # accumulate + + Sum over range with operator+ + + Also has functional versions http://www.cplusplus.com/reference/numeric/accumulate/ + */ + { + { + std::vector v{2, 0, 1}; + assert(std::accumulate(v.begin(), v.end(), 0) == 3); + assert(std::accumulate(v.begin(), v.end(), 10) == 13); + } + + // The functional version can be used to add up arrays. + // http://stackoverflow.com/questions/26941943/how-to-add-all-numbers-in-an-array-c + { + int a[] = {1, 3, 5, 7, 9}; + assert(std::accumulate(std::begin(a), std::end(a), 0, std::plus()) == 25); + } + } + + /* + # find + + Return iterator to first found element. + */ + { + std::vector v{2,0,1}; + unsigned int pos; + + pos = std::find(v.begin(), v.end(), 0) - v.begin(); + assert(pos == 1); + + pos = std::find(v.begin(), v.end(), 1) - v.begin(); + assert(pos == 2); + + pos = std::find(v.begin(), v.end(), 2) - v.begin(); + assert(pos == 0); + + pos = std::find(v.begin(), v.end(), 3) - v.begin(); //end() returned + assert(pos == v.size()); + } + + /* + # find_if + + Like find, but using an arbitrary condition on each element instead of equality. + + Consider usage with C++11 lambdas and functional. + */ + { + std::vector v{2, 0, 1}; + assert(std::find_if (v.begin(), v.end(), odd) == --v.end()); + } + + /* + # binary_search + + Container must be already sorted. + + Log complexity. + + Only states if the element is present or not, but does not get its position. + + If you want to get the position of those items, use `equal_range`, `lower_bound` or `upper_bound`. + */ + { + + std::vector v{0, 1, 2}; + assert(std::binary_search(v.begin(), v.end(), 1) == true); + assert(std::binary_search(v.begin(), v.end(), 3) == false); + assert(std::binary_search(v.begin(), v.end() - 1, 2) == false); + } + + /* + # lower_bound + + Finds first element in container which is not less than val. + */ + { + std::vector v{0, 2, 3}; + auto it = std::lower_bound(v.begin(), v.end(), 1); + assert(it - v.begin() == 1); + } + + /* + # upper_bound + + Finds first element in container is greater than val. + */ + { + std::vector v{0, 1, 2}; + auto it = std::upper_bound(v.begin(), v.end(), 1); + assert(it - v.begin() == 2); + } + + /* + # equal_range + + Finds first and last location of a value iniside a ranged container. + + Return values are the same as lower_bound and upper_bound. + + log complexity. + */ + { + std::vector v{0, 1, 1, 2}; + std::vector::iterator begin, end; + std::tie(begin, end) = std::equal_range(v.begin(), v.end(), 1); + assert(begin - v.begin() == 1); + assert(end - v.begin() == 3); + } + + // # count + { + std::vector v{2,1,2}; + assert(std::count(v.begin(), v.end(), 0) == 0); + assert(std::count(v.begin(), v.end(), 1) == 1); + assert(std::count(v.begin(), v.end(), 2) == 2); + } + + + // # max_element #min_element + { + std::vector v{2,0,1}; + assert(*std::max_element(v.begin(), v.end()) == 2); + assert(*std::min_element(v.begin(), v.end()) == 0); + } + + /* + # advance + + Advance iterator by given number. + + If random access, simply adds + N. + + Else, calls `++` N times. + + Advantage over `+`: only random access containers support `+`, + but this works for any container, allowing one to write more general code. + + Beware however that this operation will be slow for non random access containers. + */ + { + std::vector v{0, 1, 2}; + auto it = v.begin(); + std::advance(it, 2); + assert(*it == 2); + } + +#if __cplusplus >= 201103L + /* + # next + + Same as advance, but returns a new iterator instead of modifying the old one. + */ + { + std::vector v{0, 1, 2}; + auto it(v.begin()); + auto itNext = std::next(it, 2); + assert(*it == 0); + assert(*itNext == 2); + } +#endif + + /* + # priority queue + + Offers `O(1)` access to the smalles element. + + Other operatoins vary between `O(n)` and `O(1). + + Most common implementaions are via: + + - binary heap + - fibonacci heap + + Boost offers explicit heap types: fibonacci, binary and others. + + But no guarantees are made. + + As of C++11, does not support the increase key operation. + + A binary heap without increase key can be implemented via the heap function family under algorithm. + */ + + /* + # heap + + Binary heap implementation. + + + + In short: + + - getting largest element is O(1) + - removing the largest element is O(lg) for all implementation + - other operations (insertion) may be O(1) or O(lg) depending on the implementation. + + this makes for a good priority queue. + Exact heap type is not guaranteed. As of 2013, it seems that most implementations use binary heaps. + + For specific heaps such as Fibonacci, consider [Boost](http://www.boost.org/doc/libs/1_49_0/doc/html/heap.html). + + + + There is no concrete heap data structure in C++: + only heap operations over random access data structures. + This is why this is under algoritms and is not a data structure of its own. + + There is however a `priority_queue` stdlib container. + + Why random access structure is needed: + */ + { + int myints[]{10, 20, 30, 5, 15}; + std::vector v(myints, myints + 5); + + /* + # make_heap + + Make random access data structure into a heap. + + This changes the element order so that the range has heap properties + + Worst case time: $O(n)$. + */ + std::make_heap(v.begin(), v.end()); + assert(v.front() == 30); + + /* + # pop_heap + + Remove the largest element from the heap. + + That element is moved to the end of the data structure, but since the + heap should have its length reduced by one, that element will then be out of the heap. + + Assumes that the input range is already a heap (made with `make_heap` for example). + */ + std::pop_heap(v.begin(), v.end()); + + //the element still exists on the data structure + assert(v.back() == 30); + + //the second largest element hat become the largets + assert(v.front() == 20); + + //remove the element from the data structure definitively + v.pop_back(); + + /* + # push_heap + + Insert element into a heap. + + Assumes that: + + - the range 0 - (end - 1) was already a heap + - the new element to be inserted into that heap is at end. + */ + + //add the new element to the data structure + v.push_back(99); + + //reorganize the data so that the last element will be placed in the heap + std::push_heap(v.begin(), v.end()); + + assert(v.front() == 99); + + /* + # sort_heap + + Assumes that the input range is a heap, and sorts it in increasing order. + + The assumption that we have a heap allows for $O(ln)$ sorting, + much faster than the optimal bound $O(n log n)$. + + This is exactly what the heapsort alrotithm does: make_heap and then sort_heap. + */ + + std::sort_heap(v.begin(), v.end()); + //assert(v) + //v == 5 10 15 20 99 + } +} diff --git a/cpp/atomic.cpp b/cpp/atomic.cpp index 394e337..418963b 100644 --- a/cpp/atomic.cpp +++ b/cpp/atomic.cpp @@ -4,15 +4,16 @@ More restricted than mutex as it can only protect a few operations on integers. But if that is the use case, may be more efficient. + + On GCC 4.8 x86-64, using atomic is a huge peformance improvement + over the same program with mutexes (5x). */ #include "common.hpp" #if __cplusplus >= 201103L -const int NUM_THREADS = 1000; -const int NUM_ITERS = 1000; -std::atomic_int global(0); +std::atomic_long global(0); void threadMain() { for (int i = 0; i < NUM_ITERS; ++i) { @@ -25,17 +26,11 @@ int main() { #if __cplusplus >= 201103L std::thread threads[NUM_THREADS]; int i; - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) threads[i] = std::thread(threadMain); - } - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) threads[i].join(); - } - assert(global.load() == NUM_THREADS * NUM_ITERS); - // Same as above through `operator T`. assert(global == NUM_THREADS * NUM_ITERS); #endif diff --git a/cpp/atomic_bool.cpp.off b/cpp/atomic_bool.cpp.off new file mode 100644 index 0000000..2d20689 --- /dev/null +++ b/cpp/atomic_bool.cpp.off @@ -0,0 +1,24 @@ +/* +# atomic + + bool is not considered as integral in C++11 + + You have to use other alternatives for the operations. + + http://stackoverflow.com/questions/30254582/c-stdatomicboolfetch-or-not-implemented +*/ + +#include "common.hpp" + +class myclass { + volatile std::atomic flag; + public: + myclass(): flag(false) {} + bool get_flag() { return flag; } + bool try_set() { + return !flag.fetch_or(1); + } + void reset() { + flag = false; + } +}; diff --git a/cpp/attributes.cpp b/cpp/attributes.cpp new file mode 100644 index 0000000..904baf8 --- /dev/null +++ b/cpp/attributes.cpp @@ -0,0 +1,32 @@ +/* +# Attributes + + C++11 introduces a generalized attribute syntax. + + Similar syntaxes have been rmplemented as extensions for a long time in GCC via `__attribtes__` + and in Microsoft with `#pragma`. Now some have been standardized! + + Intended only for functions which don't change behaviour: only to help + compilers optimize or geneate better error messages. + + http://www.stroustrup.com/C++11FAQ.html#attributes + + Attributes can be defined for various objects, and there are 2 standard ones: + `noreturn` and `carries_dependency` + + C11 also has some attributes like `_Noreturn`, but no generalized syntax. +*/ + +#include "common.hpp" + +#if __cplusplus >= 201103L +void noreturn_func [[ noreturn ]] () { throw 1; } +#endif + +int main() { +#if __cplusplus >= 201103L + try { + noreturn_func(); + } catch (int i) {} +#endif +} diff --git a/cpp/common.hpp b/cpp/common.hpp index a5866fa..098dd48 100644 --- a/cpp/common.hpp +++ b/cpp/common.hpp @@ -27,7 +27,6 @@ #include // #include // partial sums, differences on std::vectors of numbers #include // ostream -#include // #include // multiset, set #include // getline, string #include // stringstream @@ -35,13 +34,14 @@ #include // tuple #include // unordered_map, unordered_multimap #include // forward, get, pair, size_t, type_info -#include +#include // vector #include #if __cplusplus >= 201103L #include // array #include // nanoseconds #include // mutex +#include // regex #include // thread #include // type_index #endif @@ -76,9 +76,17 @@ // Keeps a list of functions that called it for testing purposes. static std::vector callStack; -void printCallStack() { +static void printCallStack() { std::cout << "callStack:" << std::endl; for (auto& s : callStack) std::cout << s << std::endl; std::cout << "END callStack" << std::endl; } + +// Global thread parameters. +static const int NUM_THREADS = 1000; +static const int NUM_ITERS = 1000; +static const int NUM_OPS = NUM_THREADS * NUM_ITERS; + +// Misc. +static bool odd(int i){ return (i % 2) == 1; } diff --git a/cpp/containers.md b/cpp/containers.md new file mode 100644 index 0000000..27acc78 --- /dev/null +++ b/cpp/containers.md @@ -0,0 +1,18 @@ +# Containers + +The stdlib furnishes several containers. + +It is a very important part of an algorithm to choose the right container for the task. + +As of C++11, most containers are abstract, that is, only specify which operations it supports. + +For example, a `UnorderedMap` could be implemented both as a hash map or a RB tree concrete data structures, but is always supports the same operations: insert, remove, and so on. + +The major data structures which you must know about in order of decreasing usefulness are: + +- std::vector +- set +- map +- list +- deque +- heap and priority queue diff --git a/cpp/deque.cpp b/cpp/deque.cpp new file mode 100644 index 0000000..0875429 --- /dev/null +++ b/cpp/deque.cpp @@ -0,0 +1,23 @@ +/* +# deque + + Double ended queue. + + Random access. + + Very similar interface to std::vector, except that: + + - insertion to front is O(1) + - there is no guarantee of inner storage contiguity + + Discussion on when to use deque or std::vector: + http://stackoverflow.com/questions/5345152/why-would-i-prefer-using-vector-to-deque + + It is controversial if one should use deque or std::vector as the main generic container. +*/ + +#include "common.hpp" + +int main() { + // TODO example +} diff --git a/cpp/function.cpp b/cpp/function.cpp index 709bbbd..0365ff0 100644 --- a/cpp/function.cpp +++ b/cpp/function.cpp @@ -90,7 +90,7 @@ int def_no_argname(int, int) { return 2; } */ int main() { - // # overload + // # Overload { overload(1); assert(callStack.back() == "overload(int)"); @@ -137,6 +137,15 @@ int main() { */ + + /* + # volatile overload + + Functions that differ by `volatile` can be overloaded: + + - http://stackoverflow.com/questions/10242578/volatile-overloading + */ + {} } /* @@ -145,7 +154,7 @@ int main() { This probably exists for method overridding. */ { - assert(def_no_argname(0) == 1); - assert(def_no_argname(0, 0) == 2); + assert(def_no_argname(0) == 1); + assert(def_no_argname(0, 0) == 2); } } diff --git a/cpp/functional.cpp b/cpp/functional.cpp new file mode 100644 index 0000000..1c173d5 --- /dev/null +++ b/cpp/functional.cpp @@ -0,0 +1,66 @@ +/* +# functional + + Convenient simple functions. + + They implement `operator()`, which allows you to pass them to + other stdlib functions that take functions as arguments. +*/ + +#include "common.hpp" + +int main() { + /* + # bind2nd + + TODO Deprecated? + + Tranform a function that takes two arguments into a function that takes only the first. + + Useful with stdlib functions that must take functions that take a single argument, + but you want to pass an extra parameter to that function. + */ + { + /* + std::vector v = {2, 0, 1}; + assert(std::find_if ( + v.begin(), + v.end(), + std::bind2nd([](int i, int param){return i == param + 1;}, 1) + ) == v.begin()); + */ + } + + /* + # plus + */ + { + assert(std::plus()(1, 2) == 3); + } + +#if __cplusplus >= 201103L + /* + # hash + + http://www.cplusplus.com/reference/functional/hash/ + + The stdlib furnishes overloaded hash functions for stdlib containers. + + Those functions are implemented as callable classes that implement `()`. + + For base types, those hashes are found under the `functional`. + + For std::vectors, only `std::vector` has a template. + + For other types, they are found in the same header that defines those types: + ex: hash for std::vectors is under ``. + + Returns a `size_t` result. + */ + { + std::cout << "hash" << std::endl; + std::cout << " 1 = " << std::hash()(1) << std::endl; + std::cout << " string abc = " << std::hash()("abc") << std::endl; + } +#endif +} diff --git a/cpp/interactive/Makefile b/cpp/interactive/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/cpp/interactive/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/cpp/interactive/common.hpp b/cpp/interactive/common.hpp new file mode 120000 index 0000000..847f2e9 --- /dev/null +++ b/cpp/interactive/common.hpp @@ -0,0 +1 @@ +../common.hpp \ No newline at end of file diff --git a/cpp/interactive/sleep_for.cpp b/cpp/interactive/sleep_for.cpp new file mode 100644 index 0000000..e08ada0 --- /dev/null +++ b/cpp/interactive/sleep_for.cpp @@ -0,0 +1,14 @@ +/* +# sleep_for +*/ + +#include "common.hpp" + +int main() { + int i = 0; + while (1) { + std::cout << i << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + i++; + } +} diff --git a/cpp/iterator.cpp b/cpp/iterator.cpp new file mode 100644 index 0000000..65c5b74 --- /dev/null +++ b/cpp/iterator.cpp @@ -0,0 +1,361 @@ +/* +# iterator + + Iteration could be done with random access in certain data structures with a for i loop. + + Iterators are better becase you can also use them for structures without random access, + so if you decide to change structures in the future the job will be much easier. + + # iterator categories + + Iterators are categorized depending on the operations they can do: + + + + The clases are (from least to most versatile): + + - Input Output + - Forward + - Bidirectional + - Random Access + + The most versatile iterators (random access) behave much like pointers, + and overload most pointer operations such as integer increment `it + 1` and + pointer dereference `*it` in a similar way to pointers. + + Those classes are not language enforced via inheritance like in Java, + but could be used by programmers to implement typedefs that explain + the types of operations permitted. So if you are going to use a typedef + solution not to tie yourself to a given container, consider naming the + typdefed as one of the classes to indicate the operationt can do: + + typedef random_it std::vector::iterator; + + It is possible to retreive the class of an interator via `std::iterator_traits::interator_category`. +*/ + +#include "common.hpp" + +int main() { + // Before C++11: begin and end were the only way to use iterators. + // After C++11; the range based syntax is the best way to use them. + { + /* + # forward iteration + + Can be done on all containers. + + # begin + + Returns an iterator to the first element. + + # end + + Returns an iterator to the first element *after* the last. + */ + { + std::vector v{1, 2, 0}; + int i = 0; + int is[]{1, 2, 0}; + + for (auto it = v.begin(); it != v.end(); ++it) { + assert(*it == is[i]); + ++i; + } + } + + /* + # backwards iteration + + Can only be done on biderectional containers. + + # rbegin + + Reversed begin. + + Returns a `reverse_iterator` that points to the last emlement. + + ++ on reversed iterators decreases them. + + # rend + + Returns a reversed iterator to the element before the first. + */ + { + std::vector v{1, 2, 0}; + int i; + int is[]{1, 2, 0}; + + i = 2; + for (auto it = v.rbegin(); it != v.rend(); ++it) { + assert(*it == is[i]); + //cout << *it << endl; + --i; + } + } + } + + /* + # range based for loop #foreach + + C++11 + + Like python foreach or Java improved-for loop. + + This is the best way to iterate a container with C++11. + + Much easier to write or read. + + Also have the advantage that you don't need to specify iterator type! + + Behind the scenes, this method is still based on iterators, + and the class to be iterated needs to implement: + + - begin() + - end() + + And the iterator returned must implement: + + - operator++() + - operator!=() + - operator*() + */ + { +#if __cplusplus >= 201103L + //forward + { + // If `int&` is used, no useless copies are made. + // and the vector can be modified directly. + { + std::vector v{1, 2, 0}; + int is[]{1, 2, 0}; + int i = 0; + for (int& iv : v) { + assert(iv == is[i]); + //cout << iv << endl; + iv++; + i++; + } + assert((v == std::vector{2, 3, 1})); + } + + // Without `&`, makes copies of each element. + // Usually not what we want. + { + std::vector v{1, 2, 0}; + int is[]{1, 2, 0}; + int i = 0; + for (int iv : v) { + assert(iv == is[i]); + //cout << iv << endl; + iv++; + i++; + } + assert((v == std::vector{1, 2, 0})); + } + + // Less code duplication with auto. + // This is the best way to do it. + { + std::vector v{1, 2, 0}; + int is[]{1, 2, 0}; + int i = 0; + for (auto& iv : v) { + assert(iv == is[i]); + //cout << *it << endl; + i++; + } + } + } + + /* + # range based for loop for arrays + + Also works for bare arrays for which the size is known at compile time! + */ + { + { + int is[]{1, 2}; + for (int& i : is) { + i *= 2; + } + assert(is[0] == 2); + assert(is[1] == 4); + } + + /* + does not work for dynamic memory since + there would be no way to know the array size at compile time + */ + { + //int *is = new int[2]; + //is[0] = 1; + //is[0] = 2; + //for (int &i : is) { + // i *= 2; + //} + //delete[] is; + } + } +#endif + /* + backwards + + TODO possible? Seems not out of the C++11 box: + + Auto is a lifesaver here to avoid typing the iterator type. + */ + { + std::vector v = {1, 2, 0}; + int i; + int is[] = {1, 2, 0}; + + //forward + { + i = 2; + for (auto it = v.rbegin(); it != v.rend(); ++it) { + assert(*it == is[i]); + //cout << *it << endl; + i--; + } + } + } + } + + /* + # generic containers + + There is no standard iterator independent from container. + + This can be done via type erasure techinques. + + But would mean loss of performance because of lots of polymorphic calls + and stdlib is obssessed with performance. + + The best solution seems to use typedefs: + + typedef it_t std::vector::iterator; + + And then if ever your container changes all you have to do is modify one single typedef: + + typedef it_t set::iterator; + + TODO isnt auto and range based for a better solution in c++11? + */ + { + std::vector v{1, 2}; + std::set s{1, 2}; + std::vector::iterator itVec(v.begin()); + std::set::iterator itSeti(s.begin()); + + // Does not exist: + + //iterator itVec = v.begin(); + //iterator itSeti = s.begin(); + + // Best workaround is using auto: + + auto vit(v.begin()); + auto sit(v.begin()); + } + + // No born checking is done + { + std::vector v{1, 2}; + + // Last element. + *(v.end() - 1); + + // After last element no born check. + *(v.end()); + + // No such method. + //(v.end().hasNext()); + } + + /* + Base pointers and arrays can be used anywhere iterators can. + + The stdlib functions have specializations for pointers. + + + */ + { + int is[]{2, 0, 1}; + int j = 0; + for (auto& i : is) { + assert(i == is[j]); + j++; + } + } + + /* + # size_t for slt containers + + See size_type. + + # size_type + + Random access containers such as std::vectors, strings, etc have a `size_type` member typedef + that represents a type large enough to hold its indexes. + + For arrays, this type is exactly the C `size_t`. + + For a std::vector, it will also probably be `size_t`, since std::vectors are array backed, + but using `size_type` gives more generality. + + This type is returned by methods such as `size()`. + */ + { + std::vector v{2, 0, 1}; + std::vector::size_type i(1); + v[i] = 1; + } + + /* + # iterator_traits + + Contain information about iterators. + + This allows to create template functions that take generic iterators independent of the + exact container type as is the case for many function sunder ``. + */ + { + //value_type + //pointer + //reference + { + typedef std::iterator_traits::iterator>::value_type ValueType; + typedef std::iterator_traits::iterator>::pointer Pointer; + typedef std::iterator_traits::iterator>::reference Reference; + assert(typeid(ValueType) == typeid(int)); + assert(typeid(Pointer) == typeid(int*)); + assert(typeid(Reference) == typeid(int&)); + } + + /* + # difference_type + + The type returned on a difference between two pointers. + + Unlike size_type, this value is signed, since the difference may well be negative. + */ + { + typedef typename std::iterator_traits::iterator>::difference_type DifferenceType; + std::vector v{0, 1}; + assert(typeid(v.end() - v.begin()) == typeid(DifferenceType)); + } + + /* + # iterator_category + + iterator_category is a struct *type*, not a value. + + Therefore, in order to compare it one must use `typeid`. + */ + { + assert(typeid(std::iterator_traits::iterator>::iterator_category) + == typeid(std::random_access_iterator_tag)); + } + } +} diff --git a/cpp/list.cpp b/cpp/list.cpp new file mode 100644 index 0000000..df7b6f5 --- /dev/null +++ b/cpp/list.cpp @@ -0,0 +1,47 @@ +/* +# list + + Doubly linked list. + + Advantages over std::vector: fast inservion and removal from middle. + + Unless you really need those operations fast, don't use this data structure. + + No random access. + +# forward_list + + Like list, but singly linked, and therefore not backwards interable. +*/ + +#include "common.hpp" + +int main() { + //initializer list constructor + { + std::list l{0, 1}; + } + + // # emplace + { + std::list l{0, 1}; + l.emplace(++l.begin(), 2); + assert(l == std::list({0, 2, 1})); + } + + // # remove: remove all elements with a given value from list + { + std::list l{0, 1, 0, 2}; + l.remove(0); + assert(l == std::list({1, 2})); + } + + // # splice: transfer elements from one list to another + { + std::list l{0, 1}; + std::list l2{2, 3}; + l.splice(++l.begin(), l2); + assert(l == std::list({0, 2, 3, 1})); + assert(l2 == std::list()); + } +} diff --git a/cpp/main.cpp b/cpp/main.cpp index 3c4b3be..886ca09 100644 --- a/cpp/main.cpp +++ b/cpp/main.cpp @@ -2,69 +2,14 @@ C++ cheatsheat. Features which are identical to C will not be described. -*/ -/* -# Linux specifics - - The main c++ lib on linux is the GNU Standard C++ Library. - - Shared object name: `libstdc++.so`. - - Website: - - Get source code: seems to be on the same tree as gcc? - - git clone git://gcc.gnu.org/git/gcc.git - - - the so is usually located at - - /usr/lib/i386-linux-gnu/libstdc++.so.X - - If it is not there then - - locate libstdc++ - - - std headers are usually located at - - /usr/include/c++/4.X/`. - - If not, try: - - locate /iostream - - - the Ubuntu package is called `libstdc++6.X`. `dpkg -l | grep libstd` - - With `g++` the C++ standard library is linked against automatically. - This does not happen when compiling with `gcc`, and is one of the many reasons why you should use `g++` - whenever compiling C++ instead of `gcc`. +This file is being split up into smaller files. */ #include "common.hpp" class Class {}; -// struct - - template - struct BaseStruct { - T i; - BaseStruct(T i) : i(i) {} - - protected: - int iProtected; - - private: - int iPrivate; - }; - - struct DerivedStruct : BaseStruct { - DerivedStruct(int i) : BaseStruct(i) { - iProtected = i; - } - }; - - // # global scope int global = 0; @@ -84,31 +29,6 @@ class Class {}; //if (1){} //callStack.push_back("global"); -// #preprocessor - - // The C++ preprocessor it Turing complete by mistake. - // - // This leads to the concept of Template Metaprogramming, - // which allows you to write code that is ultra DRY / runs fast but is very large. - // TODO confirm. - -// string - - std::vector &split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - return elems; - } - - std::vector split(const std::string &s, char delim) { - std::vector elems; - split(s, delim, elems); - return elems; - } - //File IO void ios_write_fail(std::string path) { @@ -131,25 +51,6 @@ class Class {}; ifs.close(); } -//to_str - - template - std::string map_to_str(std::map map) { - std::stringstream result; - for (auto& pair : map) { - result << pair.first << ":" << pair.second << ", "; - } - return result.str(); - } - -// Attributes. - - void noreturn_func [[ noreturn ]] () { throw 1; } - -// Misc - - bool odd(int i){ return (i % 2) == 1; } - /* # main @@ -212,26 +113,15 @@ int main() { } /* - # assign operator + # Assign operator - Unlike in C, C++ assign operator returns lvalues! + Unlike in C, the C++ assign operator returns lvalues. TODO rationale. Related to return refs from functions? */ { int i = 0, j = 1, k = 2; - - (i = j) = k; - /*^^^^^^^^^ - | - returns a lvalue pointing to `i` - - Therefore is the same as: - - i = j; - i = k; - */ - + (i = j) = k; assert(i == 2); assert(j == 1); assert(k == 2); @@ -371,416 +261,10 @@ int main() { } } -#if __cplusplus >= 201103L - -#endif - - /* - # struct - - Structs in C++ are very similar to classes: support access modifiers, - inheritance, constructors, templates, etc. - - The major difference between them is that the default access modifier for structs - is public, while for classes it is private. - - This is why structs are used on many simple short language examples: - no public line is needed. - - The Google C++ style guide recommends using struct only if there is no constructors, - and classes otherwise. - - - */ - { - struct DerivedStruct s(1); - assert(s.i == 1); - } - - /* - # RTTI - - Run time type information. - - Any function that gets class information explicitly at runtime: - - - `typeid` - - `dynamic_cast` - - Google style 3.26 discourages this, since if you really need it your design is probably flawed. - - Also using typeid on variables means that extra meta data must be kept about those variables. - */ - - /* - # typeid - - Get type of variables. - - Can be done for both types and variables of the type. - - Returns objects of `type_info` - - # type_info - - Type returned by `typeid`. - */ - { - /* - typeid returns `type_info`. - - However copy and assign for type_info are private, - so the following fails. - */ - { - //std::type_info t = typeid(int); - //std::type_info t(typeid(int)); - } - - // type_info implements `==` and `!=`. - // - // typeid's of different types are always different. - { - int i, i1; - int& ia = i; - Class c; - - assert(typeid(i) == typeid(int) ); - assert(typeid(ia) == typeid(int&)); - assert(typeid(i) == typeid(i1) ); - assert(typeid(i) != typeid(c) ); - } - - /* - `name`: return a string representation of the type. - - The exact string is implementation defined. - - `name()` is implementation defined. - - On GCC, you can demangle with `__cxa_demangle`: - http://stackoverflow.com/questions/4465872/why-typeid-name-returns-weird-characters-using-gcc - */ - { - std::cout << "typeid(int).name() = " << typeid(int).name() << std::endl; - } - - // before: - // hash_code: return a size_t hash of the type - } - - /* - # type_index - - Wrapper around type_info that allows copy and assign. - */ - { - std::type_index t = typeid(int); - } - - /* - # type_traits - - - */ - { - } - // # stdlib // // # Standard library { - // # string - { - // Initialize from string literal. - { - std::string s = "abc"; - } - - // cout works as expected. - { - std::string s = "abc"; - std::stringstream oss; - oss << s; - assert(oss.str() == "abc"); - } - - /* - # + for strings - - # cat - - # concatenate. - - Creates a new string. - - The only way to do inline this without creating a new string seems to be by using stringstream. - http://stackoverflow.com/questions/662918/how-do-i-concatenate-multiple-c-strings-on-one-line - */ - { - std::string s = "ab"; - std::string s1 = "cd"; - assert(s + s1 == "abcd"); - assert(s + "cd" == "abcd"); - assert("cd" + s == "cdab"); - } - - // length - { - std::string s = "abc"; - assert(s.length() == 3); - } - - { - std::string s = "abc"; - s[0] = 'A'; - assert(s == "Abc"); - - // BAD: no born check! Compiles. - //s[3] = 'd'; - } - - /* - # lowercase - - http://stackoverflow.com/questions/313970/stl-string-to-lower-case - */ - { - // Best stdlib way with transform: - std::string s = "AbCd1_"; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - assert(s == "abcd1_"); - - //Boost has a single function: boost::algorithm::to_lower(str); - } - - /* - # c_str - - Convert std::string to C null terminated char* string. - */ - { - std::string s = "abc"; - assert((std::strcmp(s.c_str(), "abc")) == 0); - } - - // # substring - { - std::string s = "abcde"; - assert(s.substr(1, 3) == "bcd"); - } - - // # Split at a character into array of strings. - { - // Best stdlib solution for any character: http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c - // There are shorters sstream solutions that split at whitespace. - // For Boost it's a one liner. - { - assert((split("01:23::45", ':') == std::vector{"01", "23", "", "45"})); - - std::vector v; - split("01:23::45", ':', v); - assert((v == std::vector{"01", "23", "", "45"})); - } - } - - /* - # strip - - # chomp - - # trim - - Exact same techniques as removing elements from vectors but for characters. - - It's just that those operations are so common on strings... - - http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring - */ - { - // A single character: remove and erase idiom. - // Single remove_all call in Boost. - { - std::string s = "a bc d"; - auto end = s.end(); - s.erase(std::remove(s.begin(), end, ' '), end); - assert((s == "abcd")); - } - - // Any character in a string: remove_if + custom function. std::ispunct is a typical choice. - // Single liner with boost::remove_if + is_any_of. - { - std::string s = "a,bc. d"; - auto end = s.end(); - // stdc ispunct: - s.erase(std::remove_if(s.begin(), end, ::ispunct), end); - // stdlib ispunct. Fails without the cast. - //s.erase(std::remove_if(s.begin(), end, (int(*)(int))std::ispunct), end); - assert((s == "abc d")); - } - } - - /* - # getline - - Read istream until a any given character, by default newline, and store chars read into a string. - - The other major method of getting data from streams is `operator<<`, - which generaly speaking reads until whitespace. getline is generaly saner. - - Returns the stream itself, which allows to: - - - chain calls - - do while(getline) combos, as streams can be converted to bool via the `void*()` - operator which returns a pointer type which is then converted to a boolean. - */ - { - // Up to newline. - { - std::stringstream ss; - std::string s; - ss << "ab\n\nc"; - - // The delim is removed from the string. - assert(std::getline(ss, s)); - assert(s == "ab"); - - // Empty - assert(std::getline(ss, s)); - assert(s == ""); - - // No problem if end of stream. - assert(std::getline(ss, s)); - assert(s == "c"); - - // Stream over. - assert(!std::getline(ss, s)); - } - - // The stream itself is returned. - { - std::stringstream ss; - std::string s; - std::string s2; - ss << "ab\n\nc"; - std::getline(std::getline(ss, s), s2); - assert(s == "ab"); - assert(s2 == ""); - } - - // Up to custom char. - { - std::stringstream ss; - std::string s; - ss << "ab::f"; - std::getline(ss, s, ':'); - assert(s == "ab"); - } - - // Read stream line-wise. - { - std::stringstream ss; - std::string line; - std::vector lines; - ss << "ab\n\nc"; - while (getline(ss, line)) - lines.push_back(line); - assert((lines == std::vector{"ab", "", "c"})); - } - } - - /* - # stringstream - - # basic_stringstream - - An iostream String backed implementation. - - The following is defined: - - typedef basic_stringstream stringstream; - - typedef basic_stringstream - - Very useful to test streams without creating files / stdin. - */ - { - std::stringstream oss("abcd"); - - // str does not clear the std::stringstream object - assert(oss.str() == "abcd"); - - // To clear it you could do: http://stackoverflow.com/questions/20731/in-c-how-do-you-clear-a-stringstream-variable - // Set to empty: - oss.str(""); - // Clear flags. Very important, not only for error indicators but also for end of stream. - oss.clear(); - assert(oss.str() == ""); - - // ERROR: use of deleted function because the constructor is =delete. - //oss = std::stringstream(); - } - - // Possible application: build up a huge string step by step. - // May be more efficient than concatenations which always generates new objects. - { - std::stringstream oss; - oss << "ab"; - oss << "cd"; - assert(oss.str() == "abcd"); - } - - /* - # int to string - - There are a few standard alternatives. - - - */ - { - /* - C++11 solves the question once and for all with a robust one-liner for base types. - - It is not intended however for class input. - */ -#if __cplusplus >= 201103L - assert(std::to_string(123) == "123"); -#endif - - /* - std::stringstream seems to be the best pre C++11 solution. - - It also has the advantage of working for any class that implements `operator<<`. - */ - { - std::stringstream oss; - oss << 123; - assert(oss.str() == "123"); - } - - /* - C sprintf - - Works, but uses too many conversion operations. - */ - { - char cs[16]; - std::sprintf(cs, "%d", 123); - std::string s = (cs); - assert(s == "123"); - } - } - - // #int to string - // http://stackoverflow.com/questions/7663709/convert-string-to-int-c - { - // Best C++11 error checking option: stoi -#if __cplusplus >= 201103L - assert(std::stoi("123") == 123); -#endif - } - } - /* # io @@ -853,7 +337,9 @@ int main() { } /* - # cin #stdin + # cin + + # stdin `istream` object. @@ -1034,7 +520,11 @@ int main() { } /* - # dec #hex #oct + # dec + + # hex + + # oct Control how integers are printed. */ @@ -1119,8 +609,13 @@ int main() { } } - // #Read entire file at once: stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring - // Best way seems to be to get file size, allocate, and read manually. + /* + # Read entire file at once + + http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring + + Best way seems to be to get file size, allocate, and read manually. + */ { std::ifstream ifs(path); if (ifs) { @@ -1132,8 +627,15 @@ int main() { } } - // #Copy file to another. #cp - // #rdbuf http://stackoverflow.com/questions/2141749/what-does-ifstreamrdbuf-actually-do + /* + # Copy file to another + + # cp + + # rdbuf + + http://stackoverflow.com/questions/2141749/what-does-ifstreamrdbuf-actually-do + */ { std::string data = "abc\ndef\n"; std::string src_path = "src.tmp"; @@ -1166,12 +668,15 @@ int main() { } } - // #Compare two files larger than memory. - // TODO is there an easier way than reading each? + /* + # Compare two files larger than memory. + + TODO is there an easier way than reading each? + */ { } - // #Append to file. + // # Append to file { std::ofstream ofs(path); @@ -1182,9 +687,15 @@ int main() { ios_write_fail(path); } - // #open #Reopen - // Can be used to reopen ofstream with new properties. - // Also consider clearing error flags if there can be any. + /* + # open + + # Reopen + + Can be used to reopen ofstream with new properties. + + Also consider clearing error flags if there can be any. + */ //ofs.clear() ofs.open(path, std::ios::app); if (ofs) { @@ -1274,7 +785,11 @@ int main() { } /* - # path #directory #join + # path + + # directory + + # join Like in C, no system independant path or directory operations (for the love of God...) @@ -1282,2007 +797,8 @@ int main() { Some Boost fs features were proposed on TR2. */ - -#if __cplusplus >= 201103L - /* - # static_assert - - Make assertions at compile time. - - In this way you don't waste time compiling large programs, - or do potentially dangerous runtime operations to test your program. - - Probably became possible on C++11 because of features such as `constexpr`, - which allow to better manage compile time constantness. - - - */ - { - static_assert(0 < 1, "msg"); - - // ERROR: static assertion failed - //static_assert(0 > 1, "msg"); - - std::srand(time(NULL)); - // ERROR: needs to be a constexpr - //static_assert(std::rand() >= 0); - } -#endif - - /* - # utility - - Lots of miscelaneous utilities. - - - */ - { - -#if __cplusplus >= 201103L - /* - # tuple - - Hold a ordered collection of elements. - - Each element can be of a different type. - - The length is always fixed. - */ - { - //create - { - //constructor - { - std::tuple t0(0, 'a', "a"); - } - - /* - # make_tuple - - forwards arguments to tuple constructor. - - The advantage over the constructor is that since it is a function - template argument deduction can be done, so we don't need to type in - template arguments. - - Remember that template argument deduction cannot be done for constructors. - */ - { - std::tuple t; - - //without make_tuple - t = std::make_tuple(0, 'a', "a"); - t = std::tuple(0, 'a', "a"); - - //with make_tuple - } - - //tuple from pair - { - std::tuple t2( std::pair(0, 'a')); - } - - //uniform initialization - { - std::tuple t{0, 'a', "a"}; - } - - // Fails because the tuple constructor are is `explicit`! - // TODO Rationale? - { - //std::tuple t = {0, 1}; - //std::tuple t[]{{0, 1}}; - } - } - - /* - # get - - Get single element from tuple. - - Returns references, so it is possible to modify the tuples with them. - - Copies are made from input elements - */ - { - std::tuple t0(0, "abc"); - - assert(std::get<0>(t0) == 0); - assert(std::get<1>(t0) == "abc"); - - std::get<0>(t0) = 1; - assert(std::get<0>(t0) == 1); - - std::get<1>(t0)[0] = '0'; - assert(std::get<1>(t0) == "0bc"); - } - - /* - # tie - - Unpack a tuple. - - Unpack by reference seems not to be possible: - - # ignore - - Magic that exists only to ignore one of tie outputs. - */ - { - int i; - std::string s; - std::tuple t(1, 1.5, "abc"); - std::tie(i, std::ignore, s) = t; - assert(i == 1); - assert(s == "abc"); - - // Clearly copies are made. - i = 2; - assert(std::get<0>(t) == 1); - } - - /* - Relational operators operations are implemented - - - - `<` family is lexicographical. - */ - { - std::tuple t0(0, 'a'); - std::tuple t1(0, 'a'); - std::tuple t2(1, 'b'); - std::tuple t3(-1, 'b'); - std::tuple t4(0, 'b'); - - assert(t0 == t1); - assert(t0 != t2); - assert(t0 < t2); - assert(t0 > t3); //-1 counts - assert(t0 < t4); //0 ties, 'a' < 'b' - } - - //swap contents of two tuples of same type - { - std::tuple t0(0, 'a'); - std::tuple t1(1, 'b'); - - std::tuple old_t0 = t0; - std::tuple old_t1 = t1; - - t0.swap(t1); - - assert(t0 == old_t1); - assert(t1 == old_t0); - } - } -#endif - - /* - # pair - - Particular case of tuple for two elements - - Methods which also exist for tuple will not be discussed. - - Specially important because of `map`. - */ - { - //access: can also be done via `.first` and `.second` in addition to tuple `get`. - { - std::pair p(0, 'a'); - assert(std::get<0>(p) == p.first); - assert(std::get<1>(p) == p.second); - } - } - - /* - # forward - - TODO - */ - { - } - } - - /* - # containers - - The stdlib furnishes several containers. - - It is a very important part of an algorithm to choose the right container for the task. - - As of C++11, most containers are abstract, that is, only specify which operations it supports. - - For example, a `UnorderedMap` could be implemented both as a hash map or a RB tree concrete data structures, - but is always supports the same operations: insert, remove, and so on. - - The major data structures which you must know about in order of decreasing usefulness are: - - - std::vector - - set - - map - - list - - deque - - heap and priority queue - */ - - /* - # vector - - Array backed conatiner that grows / shrinks as necessary. - - $O(1)$ random access. - - $O(n)$ element removal from interior - - $O(1)$ element append to end (amortized, $O(n)$ worst case) - - All methods that work for several SLT containers shall only be cheated here once. - */ - { - // Create - { - // Empty - { - std::vector v; - // C++11 initializer lists: - std::vector v1{}; - assert(v == v1); - } - - /* - Fill constructor. - - Make a `std::vector` with n copies of a single value. - */ - { - //copies of given object - { - assert(std::vector(3, 2) == std::vector({2, 2, 2})); - } - - //default constructed objects. int = 0. - { - assert(std::vector(3) == std::vector({0, 0, 0})); - } - } - - // Range copy. - { - std::vector v{0, 1, 2}; - std::vector v1(v.begin(), v.end()); - assert(v == v1); - } - - // From existing array. - { - int myints[]{0, 1, 2}; - std::vector v(myints, myints + sizeof(myints) / sizeof(int)); - std::vector v1 = {0, 1, 2}; - assert(v == v1); - } - } - - // Vectors have order. - { - std::vector v{0, 1, 2}; - std::vector v1{2, 1, 0}; - assert(v != v1); - } - - /* - # Contigous storage - - # Data - - Storage is required to be contiguous by TR1: - http://stackoverflow.com/questions/849168/are-stdvector-elements-guaranteed-to-be-contiguous - - C++11 introduces the `data()` method which returns a pointer to the first element. - It works even if the vector is empty. - http://stackoverflow.com/questions/6485496/how-to-get-stdvector-pointer-to-the-raw-data - - Before C++11, `&v[0]` works for non-empty vectors. - - `vector` as usual is an exception. - */ - { - std::vector v{0, 1, 2}; - assert(&v[0] == v.data()); - // True because contiguous: - assert(v.data()[1] == v[1]); - } - - // size methods - { - /* - # size - - # length of vector - - # size_type - - Number of elements in std::vector. - - This has type std::vector::size_type - */ - { - std::vector v; - assert(v.size() == 0); - v.push_back(0); - assert(v.size() == 1); - } - - /* - # resize - - If larger than current size, append given element at end. - - If smaller than current size, remove elements from end. - */ - { - // Reduce size - { - std::vector v{0, 1}; - v.resize(1); - assert((v == std::vector{0})); - } - - // Increase size - { - - // Using default constructor objects. - { - std::vector v{1}; - v.resize(3); - assert((v == std::vector{1, 0, 0})); - } - - // Using copies of given object. - { - std::vector v{1}; - v.resize(3, 2); - assert((v == std::vector{1, 2, 2})); - } - } - } - } - - // Capacity methods. - { - /* - # capacity - - Get currently allocated size. - - Different from size, which is the number of elements in the std::vector! - - At least as large as size. - - Likely to be a power of 2 on most implementations. - */ - { - std::vector v; - v.push_back(0); - v.push_back(1); - v.push_back(2); - assert(v.capacity() >= 3); - std::cout << "capacity = " << v.capacity() << std::endl; - } - - // # max_size: estimative of what your OS allows you to allocate - { - std::cout << "max_size (MiB) = " << std::vector().max_size() / (1 << 20) << std::endl; - } - - // # reserve: increase allocated size if larger than current size. - { - std::vector v; - v.reserve(3); - assert(v.capacity() >= 3); - // size() is untouched - assert(v.empty()); - } - -#if __cplusplus >= 201103L - // # shrink_to_fit - { - std::vector v{0, 1}; - v.reserve(4); - v.shrink_to_fit(); - assert(v.capacity() == 2); - } -#endif - } - - // `std::vector` stores copies of elements, not references. - { - std::string s = "abc"; - std::vector v{s}; - v[0][0] = '0'; - assert(v[0] == "0bc"); - assert(s == "abc"); - } - - // Modify. - { - { - std::vector v; - v = {0}; - v = {0, 1}; - assert((v == std::vector{0, 1})); - } - - /* - # push_back - - # append - - Push to the end of the std::vector. - - Amortized time O(1), but may ocassionaly make the std::vector grow, - which may required a full data copy to a new location if the - current backing array cannot grow. - - # push_front - - Does not exist for std::vector, as it would always be too costly (requires to move - each element forward.) Use deque if you need that. - */ - { - std::vector v; - std::vector v1; - - v.push_back(0); - v1 = {0}; - assert(v == v1); - - v.push_back(1); - v1 = {0, 1}; - assert(v == v1); - - /* - push_back makes copies with assign `=` - - If you want references, use pointers, or even better, auto_ptr. - */ - { - std::vector v; - std::string s = "abc"; - - v.push_back(s); - v[0][0] = '0'; - assert(v[0] == "0bc"); - - //s was not changed - assert(s == "abc"); - } - } - - /* - # pop_back - - Remove last element from std::vector. - - No return val. Rationale: - */ - { - std::vector v{0, 1}; - - v.pop_back(); - assert(v == std::vector{0}); - - v.pop_back(); - assert(v == std::vector{}); - } - - /* - # insert - - This operation is inneficient for `std::vector` if it is not done at the end. - - # concatenate - - The range form of insert can be used to append one vector to anoter. - */ - { - // Single element form. - { - std::vector v = {0,1}; - std::vector v1; - - v.insert(v.begin(), -1); - v1 = {-1, 0, 1}; - assert(v == v1); - - v.insert(v.end(), 2); - v1 = {-1, 0, 1, 2}; - assert(v == v1); - } - - // Range form. - { - std::vector v = {0,1}; - std::vector v1 = {2,3}; - - v.insert(v.end(), v1.begin(), v1.end()); - assert((v == std::vector{0, 1, 2, 3})); - } - } - - /* - # erase - - Remove given elements from container given iterators to those elements. - - This operation is inneficient for std::vectors, - since it may mean reallocation and therefore up to $O(n)$ operations. - - Returns a pointer to the new location of the element next to the last removed element. - */ - { - // Single element - { - std::vector v{0, 1, 2}; - auto it = v.erase(v.begin() + 1); - assert((v == std::vector{0, 2})); - assert(*it == 2); - } - - // Range - { - std::vector v{0, 1, 2, 3}; - auto it = v.erase(v.begin() + 1, v.end() - 1); - assert((v == std::vector{0, 3})); - assert(*it == 3); - } - } - - /* - # remove - - Helper to remove all elements that compare equal to a value from container. - - Does not actually remove the elements: only ensures that the beginning of the range - does not contain the item to be removed. - - Ex: - - 0, 1, 0, 2, 0, 1 - - Value to remove: `0` - - Range to remove from: - - 0, 1, 0, 2, 0, 1 - ---------- - - After the remove: - - 1, 2, X, Y, 0, 1 - ---------- - - where `X` and `Y` are trash, and not necessarily 0! - - To actually remove the items, an `erase` is needed after remove - because `remove` is not a class method and thus cannot remove items from a container. - - This is called the erase and remove idiom. - - After a remove the container becomes: - - 1, 2, 0, 1 - - # erase and remove idiom - - # remove and erase idiom - - See remove. - */ - { - // Verbose version - { - std::vector v{0, 1, 0, 2, 0, 1}; - auto end = std::next(v.end(), -2); - auto firstTrashIt = std::remove(v.begin(), end, 0); - // Unpredictable result: - std::cout << "remove:"; - for (auto& i : v) std::cout << " " << i; - std::cout << std::endl; - v.erase(firstTrashIt, end); - assert((v == std::vector{1, 2, 0, 1})); - } - - // Compact version - { - std::vector v{0, 1, 0, 2, 0, 1}; - auto end = std::next(v.end(), -2); - v.erase(std::remove(v.begin(), end, 0), end); - assert((v == std::vector{1, 2, 0, 1})); - } - } - - // # remove_if # filter - // Algorithm. Remove if a given function evaluates to true on an element. - { - std::vector v{0, 1, 2, 3, 4}; - auto end = v.end(); - v.erase(std::remove_if(v.begin(), end, odd), end); - assert((v == std::vector{0, 2, 4})); - - // Common combo with lambdas - { - std::vector v{0, 1, 2, 3, 4}; - auto end = v.end(); - v.erase(std::remove_if(v.begin(), end, - [](int i) {return i % 2 == 1;}), end); - assert((v == std::vector{0, 2, 4})); - } - } - - // # transform - // Algorithm. Replace elements by output of a function. - { - std::vector v{0, 1, 2}; - std::transform(v.begin(), v.end(), v.begin(), - [](int i) {return i * i;}); - assert((v == std::vector{0, 1, 4})); - } - - // # clear - { - std::vector v{0, 1, 2}; - v.clear(); - assert(v.size() == 0); - } - - /* - # print vector - - # vector to string - - No built-in way. - - http://stackoverflow.com/questions/4850473/pretty-print-c-stl-containers - 190 votes on question, 30 on top answer! Come on C++! - - http://stackoverflow.com/questions/1430757/c-vector-to-string?lq=1 - */ - // ERROR: no default operator `<<`. - //cout << v; - } - - // Random access is O(1) since array backed - { - - std::vector v{0, 1, 2}; - - // First element: - - assert(v.front() == 0); - assert(v.front() == v[0]); - - // Last element: - - assert(v.back() == 2); - - // Nth element: - - v[0] = 1; - assert(v[0] == 1); - - /* - BAD: just like array overflow will not change std::vector size, - and is unlikelly to give an error - */ - { - //v1[2] = 2; - } - - // #back Get reference to last element in vector. - // #front Get reference to first element in vector. - // #at Like `[]`, but does bound checking and throws `out_of_range` in case of overflow. - { - std::vector v{0, 1, 2}; - assert(v.front() == 0); - assert(v.at(1) == 1); - assert(v.back() == 2); - try { - assert(v.at(3) == 0); - } catch (std::out_of_range& e) { - } catch (...) { - assert(false); - } - } - } - - /* - # bool std::vector - - *bool std::vectors are evil!* - - The standard requires `vector` to have an specialization for bool which packs bits efficiently. - - While efficient, in order to work this specialization breaks common std::vector interfaces - that require taking addresses only in the case of this specialization, since it does not - make sense to takes addresses anymore. - - Alternatives to `vector`: - - A good alternative seem to be deque, which behaves as intended. - */ - { - // Works fine and dandy as expected. - { - std::vector v{1, 0}; - int& i(v[0]); - } - - // Does not compile!!! - { - std::vector v{true, false}; - //bool& b(v[0]); - } - - // It was not a problem with bool, the problem really is `vector`. - { - bool b[]{true, false}; - bool& b2(b[0]); - } - } - } - - /* - # deque - - Double ended queue. - - Random access. - - Very similar interface to std::vector, except that: - - - insertion to front is O(1) - - there is no guarantee of inner storage contiguity - - Discussion on when to use deque or std::vector: - - - It is controversial if one should use deque or std::vector as the main generic container. - */ - - /* - # set - - - unique elements: inserting twice does nothing - - - always ordered: $O(log)$ find / insert - - - immutable elements: it is not possible to modify an object, - one must first remove it and resinsert. - - This is so because modification may mean reordering. - */ - { - // C++11 initializer list - { - { - std::set s{1, 2, 0, 1}; - std::set s2{0, 1, 2}; - assert(s == s2); - } - - { - std::set s = {"a", "c", "b", "a"}; - std::set s1 = {"a","b", "c"}; - assert(s == s1); - } - } - - // You can modify objects if you store pointers. - { - int i = 0; - std::set s; - s.insert(&i); - std::set::iterator it = s.find(&i); - *(*it) = 1; - assert(i == 1); - } - - /* - # insert - - Like for std::vector, insert makes copies. - - Return is a pair conatining: - - - if the item was not present, an iterator to the item inserted and true - - if the item was present, an iterator to the existing item inserted and false - */ - { - std::pair::iterator,bool> ret; - std::set s; - - ret = s.insert(1); - assert(ret.first == s.find(1)); - assert(ret.second == true); - - ret = s.insert(2); - assert(ret.first == s.find(2)); - assert(ret.second == true); - - ret = s.insert(0); - assert(ret.first == s.find(0)); - assert(ret.second == true); - - //item already present: - //nothing is done and returns false on the pair - ret = s.insert(1); - assert(ret.first == s.find(1)); - assert(ret.second == false); - - std::set s1 = {0, 1, 2}; - assert(s == s1); - } - - /* - # erase - - Remove element from set. - - Returns number of elements removed. - */ - { - std::set s = {0, 1, 2}; - - assert(s.erase(1) == 1); - std::set s2 = {0, 2}; - assert(s == s2); - - assert(s.erase(1) == 0); - } - - // ERROR no random access since it uses bidirection iterator. - { - //cout << s[0] << endl; - } - - //size - { - std::set s; - assert(s.size() == 0); - s.insert(0); - assert(s.size() == 1); - } - - /* - iterate - - Biderectional iterator. - - Always sorted. - */ - - /* - find - - If found, returns an iterator pointing to the element. - Else, returns `map::end()` - - find is `log n` time since the container is ordered. - - log n time complexity since always sorted - */ - { - std::set s = {0, 1, 2}; - std::set::iterator it; - - it = s.find(1); - assert(*it == 1); - - it = s.find(3); - assert(it == s.end()); - } - - /* - count - - Count how many times an item is in the set. - - Can only return 1 or 0. - - Equivalent to doing a find. - */ - { - std::set s = {1, 2, 0, 1}; - assert(s.count(1) == 1); - assert(s.count(3) == 0); - } - } - - /* - # hashmap - - There seems to be no explicit hashmap container, only a generic map interface, - - See map. - - Nonstandard `hash_map` already provided with gcc and msvc++. - It is placed in the `std::` namespace, but it is *not* ISO. - - # map - - - - Also comes in an unordered version `unordered_map`. - - Ordered. - - Also comes in an multiple value input version `multimap`. - - Does not require a hash function. Usually implemented as a self balancing tree such as a rb tree. - - # unordered_map - - TODO complexity comparison to map. - */ - { - /* - The initializer list constructor makes things very easy. - */ - { - std::map m{ - {0, "zero"}, - {1, "one"}, - {2, "two"}, - }; - } - - /* - emplace - - put a value pair into the map without creating the pair explicitly - - needs gcc 4.8: - */ - { - //std::map m; - //m.emplace(0, "zero"); - //m.emplace(1, "one"); - //m.emplace(2, "two"); - } - - /* - # insert - - Insert pair into map. - - The return value is similar to that of a set insertion with respec to the key. - */ - { - std::map m; - std::pair::iterator,bool> ret; - - ret = m.insert(std::pair(0, "zero")); - assert(ret.first == m.find(0)); - assert(ret.second == true); - - ret = m.insert(std::pair(1, "one")); - assert(ret.first == m.find(1)); - assert(ret.second == true); - - //key already present - ret = m.insert(std::pair(1, "one2")); - assert(m[1] == "one"); - assert(ret.first == m.find(1)); - assert(ret.second == false); - } - - /* - iterate - - Map is ordered. - - It is iterated in key `<` order. - - Iteration returns key value pairs. - */ - { - std::map m{ - {1, "one"}, - {0, "zero"}, - }; - - int i = 0; - int is[] = {0, 1}; - for (auto& im : m) { - assert(im.first == is[i]); - //cout << im->second << endl; - ++i; - } - assert(i == 2); - assert(map_to_str(m) == "0:zero, 1:one, "); - } - - /* - [] operator - - get value from a given key - - WARNING: if the key does not exist, it is inserted with a value with default constructor. - - This can be avoided by using `find` instead of `[]`. - */ - { - std::map m{ - {0, "zero"}, - {1, "one"}, - }; - - assert(m[0] == "zero"); - assert(m[1] == "one"); - - //inserts `(3,"")` because `""` is the value for the default string constructor - assert(m[2] == ""); - assert(m.size() == 3); - } - - /* - # find #check if in map - - Similar to `std::set` find with respect to the keys: - returns an iterator pointing to the pair which has given key, not the value. - - If not found, returns `map::end()` - - This is perferrable to `[]` since it does not insert non-existent elements. - */ - { - std::map m{ - {0, "zero"}, - {1, "one"}, - }; - - assert(m.find(0)->second == "zero"); - assert(m.find(1)->second == "one"); - - assert(m.find(2) == m.end()); - assert(m.size() == 2); - } - - /* - erase - - Remove element from map. - - Returns number of elements removed. - */ - { - int ret; - - std::map m{ - {0, "zero"}, - {1, "one"}, - }; - - std::map m2; - m2.insert(std::pair(0, "zero")); - - ret = m.erase(1); - assert(ret = 1); - - assert(m == m2); - - ret = m.erase(1); - assert(ret == 0); - } - } - - /* - # list - - Doubly linked list. - - Advantages over std::vector: fast inservion and removal from middle. - - Unless you really need those operations fast, don't use this data structure. - - No random access. - - # forward_list - - Like list, but singly linked, and therefore not backwards interable. - */ - { - //initializer list constructor - { - std::list l{0, 1}; - } - - // # emplace - { - std::list l{0, 1}; - l.emplace(++l.begin(), 2); - assert(l == std::list({0, 2, 1})); - } - - // # remove: remove all elements with a given value from list - { - std::list l{0, 1, 0, 2}; - l.remove(0); - assert(l == std::list({1, 2})); - } - - // # splice: transfer elements from one list to another - { - std::list l{0, 1}; - std::list l2{2, 3}; - l.splice(++l.begin(), l2); - assert(l == std::list({0, 2, 3, 1})); - assert(l2 == std::list()); - } - } - - /* - # iterator - - Iteration could be done with random access in certain data structures with a for i loop. - - Iterators are better becase you can also use them for structures without random access, - so if you decide to change structures in the future the job will be much easier. - - # iterator categories - - Iterators are categorized depending on the operations they can do: - - - - The clases are (from least to most versatile): - - - Input Output - - Forward - - Bidirectional - - Random Access - - The most versatile iterators (random access) behave much like pointers, - and overload most pointer operations such as integer increment `it + 1` and - pointer dereference `*it` in a similar way to pointers. - - Those classes are not language enforced via inheritance like in Java, - but could be used by programmers to implement typedefs that explain - the types of operations permitted. So if you are going to use a typedef - solution not to tie yourself to a given container, consider naming the - typdefed as one of the classes to indicate the operationt can do: - - typedef random_it std::vector::iterator; - - It is possible to retreive the class of an interator via `std::iterator_traits::interator_category`. - */ - { - // Before C++11: begin and end were the only way to use iterators. - // After C++11; the range based syntax is the best way to use them. - { - /* - # forward iteration - - Can be done on all containers. - - # begin - - Returns an iterator to the first element. - - # end - - Returns an iterator to the first element *after* the last. - */ - { - std::vector v{1, 2, 0}; - int i = 0; - int is[]{1, 2, 0}; - - for (auto it = v.begin(); it != v.end(); ++it) { - assert(*it == is[i]); - ++i; - } - } - - /* - # backwards iteration - - Can only be done on biderectional containers. - - # rbegin - - Reversed begin. - - Returns a `reverse_iterator` that points to the last emlement. - - ++ on reversed iterators decreases them. - - # rend - - Returns a reversed iterator to the element before the first. - */ - { - std::vector v{1, 2, 0}; - int i; - int is[]{1, 2, 0}; - - i = 2; - for (auto it = v.rbegin(); it != v.rend(); ++it) { - assert(*it == is[i]); - //cout << *it << endl; - --i; - } - } - } - - /* - # range based for loop #foreach - - C++11 - - Like python foreach or Java improved-for loop. - - This is the best way to iterate a container with C++11. - - Much easier to write or read. - - Also have the advantage that you don't need to specify iterator type! - - Behind the scenes, this method is still based on iterators, - and the class to be iterated needs to implement: - - - begin() - - end() - - And the iterator returned must implement: - - - operator++() - - operator!=() - - operator*() - */ - { -#if __cplusplus >= 201103L - //forward - { - // If `int&` is used, no useless copies are made. - // and the vector can be modified directly. - { - std::vector v{1, 2, 0}; - int is[]{1, 2, 0}; - int i = 0; - for (int& iv : v) { - assert(iv == is[i]); - //cout << iv << endl; - iv++; - i++; - } - assert((v == std::vector{2, 3, 1})); - } - - // Without `&`, makes copies of each element. - // Usually not what we want. - { - std::vector v{1, 2, 0}; - int is[]{1, 2, 0}; - int i = 0; - for (int iv : v) { - assert(iv == is[i]); - //cout << iv << endl; - iv++; - i++; - } - assert((v == std::vector{1, 2, 0})); - } - - // Less code duplication with auto. - // This is the best way to do it. - { - std::vector v{1, 2, 0}; - int is[]{1, 2, 0}; - int i = 0; - for (auto& iv : v) { - assert(iv == is[i]); - //cout << *it << endl; - i++; - } - } - } - - /* - # range based for loop for arrays - - Also works for bare arrays for which the size is known at compile time! - */ - { - { - int is[]{1, 2}; - for (int& i : is) { - i *= 2; - } - assert(is[0] == 2); - assert(is[1] == 4); - } - - /* - does not work for dynamic memory since - there would be no way to know the array size at compile time - */ - { - //int *is = new int[2]; - //is[0] = 1; - //is[0] = 2; - //for (int &i : is) { - // i *= 2; - //} - //delete[] is; - } - } -#endif - /* - backwards - - TODO possible? Seems not out of the C++11 box: - - Auto is a lifesaver here to avoid typing the iterator type. - */ - { - std::vector v = {1, 2, 0}; - int i; - int is[] = {1, 2, 0}; - - //forward - { - i = 2; - for (auto it = v.rbegin(); it != v.rend(); ++it) { - assert(*it == is[i]); - //cout << *it << endl; - i--; - } - } - } - } - - /* - # generic containers - - There is no standard iterator independent from container. - - This can be done via type erasure techinques. - - But would mean loss of performance because of lots of polymorphic calls - and stdlib is obssessed with performance. - - The best solution seems to use typedefs: - - typedef it_t std::vector::iterator; - - And then if ever your container changes all you have to do is modify one single typedef: - - typedef it_t set::iterator; - - TODO isnt auto and range based for a better solution in c++11? - */ - { - std::vector v{1, 2}; - std::set s{1, 2}; - std::vector::iterator itVec(v.begin()); - std::set::iterator itSeti(s.begin()); - - // Does not exist: - - //iterator itVec = v.begin(); - //iterator itSeti = s.begin(); - - // Best workaround is using auto: - - auto vit(v.begin()); - auto sit(v.begin()); - } - - // No born checking is done - { - std::vector v{1, 2}; - - // Last element. - *(v.end() - 1); - - // After last element no born check. - *(v.end()); - - // No such method. - //(v.end().hasNext()); - } - - /* - Base pointers and arrays can be used anywhere iterators can. - - The stdlib functions have specializations for pointers. - - - */ - { - int is[]{2, 0, 1}; - int j = 0; - for (auto& i : is) { - assert(i == is[j]); - j++; - } - } - - /* - # size_t for slt containers - - See size_type. - - # size_type - - Random access containers such as std::vectors, strings, etc have a `size_type` member typedef - that represents a type large enough to hold its indexes. - - For arrays, this type is exactly the C `size_t`. - - For a std::vector, it will also probably be `size_t`, since std::vectors are array backed, - but using `size_type` gives more generality. - - This type is returned by methods such as `size()`. - */ - { - std::vector v{2, 0, 1}; - std::vector::size_type i(1); - v[i] = 1; - } - - /* - # iterator_traits - - Contain information about iterators. - - This allows to create template functions that take generic iterators independent of the - exact container type as is the case for many function sunder ``. - */ - { - //value_type - //pointer - //reference - { - typedef std::iterator_traits::iterator>::value_type ValueType; - typedef std::iterator_traits::iterator>::pointer Pointer; - typedef std::iterator_traits::iterator>::reference Reference; - assert(typeid(ValueType) == typeid(int)); - assert(typeid(Pointer) == typeid(int*)); - assert(typeid(Reference) == typeid(int&)); - } - - /* - # difference_type - - The type returned on a difference between two pointers. - - Unlike size_type, this value is signed, since the difference may well be negative. - */ - { - typedef typename std::iterator_traits::iterator>::difference_type DifferenceType; - std::vector v{0, 1}; - assert(typeid(v.end() - v.begin()) == typeid(DifferenceType)); - } - - /* - # iterator_category - - iterator_category is a struct *type*, not a value. - - Therefore, in order to compare it one must use `typeid`. - */ - { - assert(typeid(std::iterator_traits::iterator>::iterator_category) - == typeid(std::random_access_iterator_tag)); - } - } - } - - /* - # valarray - - Array of values. Wors much like a mathematical vector. - - Container that overloads many mathematical operations in a similar way to what Fortran does, - which may be more efficient and convenient. - - Very obscure, for several reasons: - - - other techniques achieve what it achieves - - low compiler support - - Downsides compared to vectors: - - - not resizable, so no push_back - - - */ - { - std::valarray v; - std::valarray v0{0, 1, 2}; - std::valarray v1{3, 4, 5}; - - assert(v0.sum() == 3); - assert(v0.min() == 0); - assert(v0.max() == 2); - - /* - v = v0; - v.cshift(1); - assert((v == std::valarray{1, 2, 0}).min()); - */ - - // == is elementwise equality. - // - // For equality of all elements, do `.min() == true` - { - std::valarray v0{0, 1, 2}; - std::valarray v1{0, 2, 2}; - assert(((v0 == v1) == std::valarray{true, false, true}).min()); - - } - - // +, -, *, /, etc are overloaded elementwise. - // - // They are also overloaded for contained data type. - { - assert((v0 + v1 == std::valarray{3, 5, 7}).min()); - assert((v0 + 1 == std::valarray{1, 2, 3}).min()); - } - - // Basic cmath functions are overloaded for valarray elementwise. - { - assert((abs(std::valarray{-1, 0, 1}) == std::valarray{1, 0, 1}).min()); - assert((pow(std::valarray{-2, 0, 2}, 2) == std::valarray{4, 0, 4}).min()); - } - } - - // # algorithm - { - { - assert(std::min(0.1, 0.2) == 0.1); - assert(std::max(0.1, 0.2) == 0.2); - } - - // # sort - { - std::vector v{2, 0, 1}; - std::sort(v.begin(), v.end()); - std::vector v1 = {0, 1, 2}; - assert((v == std::vector{0, 1, 2})); - } - - // # reverse - { - std::vector v{2, 0, 1}; - std::reverse(v.begin(), v.end()); - assert((v == std::vector{1, 0, 2})); - } - - /* - # swap - - Does things equivalent to: - - template void swap (T& a, T& b) - { - T c(a); a=b; b=c; - } - - However stdlib can specialize it to do operations more efficiently. - - Some stdlib classes implement swap as a method. - - Particularly important because of the copy and swap idiom. - */ - - // # randomize - { - std::vector v{2, 0, 1}; - std::random_shuffle(v.begin(), v.end()); - } - - // # copy - { - std::vector v{2, 0, 1}; - std::vector v2(5, 3); - std::copy(v.begin(), v.end(), v2.begin() + 1); - assert(v2 == std::vector({3, 2, 0, 1, 3})); - } - - /* - # equal - - Compares ranges of two containers. - */ - { - std::vector v {0, 1, 2 }; - std::vector v2{ 1, 2, 3}; - assert(std::equal(v.begin() + 1, v.end(), v2.begin())); - } - - /* - # accumulate - - Sum over range. - - Also has functional versions - */ - { - std::vector v{2, 0, 1}; - assert(std::accumulate(v.begin(), v.end(), 0) == 3); - assert(std::accumulate(v.begin(), v.end(), 10) == 13); - } - - /* - # find - - Return iterator to first found element. - */ - { - std::vector v{2,0,1}; - unsigned int pos; - - pos = std::find(v.begin(), v.end(), 0) - v.begin(); - assert(pos == 1); - - pos = std::find(v.begin(), v.end(), 1) - v.begin(); - assert(pos == 2); - - pos = std::find(v.begin(), v.end(), 2) - v.begin(); - assert(pos == 0); - - pos = std::find(v.begin(), v.end(), 3) - v.begin(); //end() returned - assert(pos == v.size()); - } - - /* - # find_if - - Like find, but using an arbitrary condition on each element instead of equality. - - Consider usage with C++11 lambdas and functional. - */ - { - std::vector v{2, 0, 1}; - assert(std::find_if (v.begin(), v.end(), odd) == --v.end()); - } - - /* - # binary_search - - Container must be already sorted. - - Log complexity. - - Only states if the element is present or not, but does not get its position. - - If you want to get the position of those items, use `equal_range`, `lower_bound` or `upper_bound`. - */ - { - - std::vector v{0, 1, 2}; - assert(std::binary_search(v.begin(), v.end(), 1) == true); - assert(std::binary_search(v.begin(), v.end(), 3) == false); - assert(std::binary_search(v.begin(), v.end() - 1, 2) == false); - } - - /* - # lower_bound - - Finds first element in container which is not less than val. - */ - { - std::vector v{0, 2, 3}; - auto it = std::lower_bound(v.begin(), v.end(), 1); - assert(it - v.begin() == 1); - } - - /* - # upper_bound - - Finds first element in container is greater than val. - */ - { - std::vector v{0, 1, 2}; - auto it = std::upper_bound(v.begin(), v.end(), 1); - assert(it - v.begin() == 2); - } - - /* - # equal_range - - Finds first and last location of a value iniside a ranged container. - - Return values are the same as lower_bound and upper_bound. - - log complexity. - */ - { - std::vector v{0, 1, 1, 2}; - std::vector::iterator begin, end; - std::tie(begin, end) = std::equal_range(v.begin(), v.end(), 1); - assert(begin - v.begin() == 1); - assert(end - v.begin() == 3); - } - - // # count - { - std::vector v{2,1,2}; - assert(std::count(v.begin(), v.end(), 0) == 0); - assert(std::count(v.begin(), v.end(), 1) == 1); - assert(std::count(v.begin(), v.end(), 2) == 2); - } - - - // # max_element #min_element - { - std::vector v{2,0,1}; - assert(*std::max_element(v.begin(), v.end()) == 2); - assert(*std::min_element(v.begin(), v.end()) == 0); - } - - /* - # advance - - Advance iterator by given number. - - If random access, simply adds + N. - - Else, calls `++` N times. - - Advantage over `+`: only random access containers support `+`, - but this works for any container, allowing one to write more general code. - - Beware however that this operation will be slow for non random access containers. - */ - { - std::vector v{0, 1, 2}; - auto it = v.begin(); - std::advance(it, 2); - assert(*it == 2); - } - -#if __cplusplus >= 201103L - /* - # next - - Same as advance, but returns a new iterator instead of modifying the old one. - */ - { - std::vector v{0, 1, 2}; - auto it(v.begin()); - auto itNext = std::next(it, 2); - assert(*it == 0); - assert(*itNext == 2); - } -#endif - - /* - # priority queue - - Offers `O(1)` access to the smalles element. - - Other operatoins vary between `O(n)` and `O(1). - - Most common implementaions are via: - - - binary heap - - fibonacci heap - - Boost offers explicit heap types: fibonacci, binary and others. - - But no guarantees are made. - - As of C++11, does not support the increase key operation. - - A binary heap without increase key can be implemented via the heap function family under algorithm. - */ - - /* - # heap - - Binary heap implementation. - - - - In short: - - - getting largest element is O(1) - - removing the largest element is O(lg) for all implementation - - other operations (insertion) may be O(1) or O(lg) depending on the implementation. - - this makes for a good priority queue. - Exact heap type is not guaranteed. As of 2013, it seems that most implementations use binary heaps. - - For specific heaps such as Fibonacci, consider [Boost](http://www.boost.org/doc/libs/1_49_0/doc/html/heap.html). - - - - There is no concrete heap data structure in C++: - only heap operations over random access data structures. - This is why this is under algoritms and is not a data structure of its own. - - There is however a `priority_queue` stdlib container. - - Why random access structure is needed: - */ - { - int myints[]{10, 20, 30, 5, 15}; - std::vector v(myints, myints + 5); - - /* - # make_heap - - Make random access data structure into a heap. - - This changes the element order so that the range has heap properties - - Worst case time: $O(n)$. - */ - std::make_heap(v.begin(), v.end()); - assert(v.front() == 30); - - /* - # pop_heap - - Remove the largest element from the heap. - - That element is moved to the end of the data structure, but since the - heap should have its length reduced by one, that element will then be out of the heap. - - Assumes that the input range is already a heap (made with `make_heap` for example). - */ - std::pop_heap(v.begin(), v.end()); - - //the element still exists on the data structure - assert(v.back() == 30); - - //the second largest element hat become the largets - assert(v.front() == 20); - - //remove the element from the data structure definitively - v.pop_back(); - - /* - # push_heap - - Insert element into a heap. - - Assumes that: - - - the range 0 - (end - 1) was already a heap - - the new element to be inserted into that heap is at end. - */ - - //add the new element to the data structure - v.push_back(99); - - //reorganize the data so that the last element will be placed in the heap - std::push_heap(v.begin(), v.end()); - - assert(v.front() == 99); - - /* - # sort_heap - - Assumes that the input range is a heap, and sorts it in increasing order. - - The assumption that we have a heap allows for $O(ln)$ sorting, - much faster than the optimal bound $O(n log n)$. - - This is exactly what the heapsort alrotithm does: make_heap and then sort_heap. - */ - - std::sort_heap(v.begin(), v.end()); - //assert(v) - //v == 5 10 15 20 99 - } - } - -#if __cplusplus >= 201103L - /* - # functional - - Do magic with functions. - - Useful with stdlib functions that take functions as arguments. - */ - { - /* - # bind2nd - - Tranform a function that takes two arguments into a function that takes only the first. - - Useful with stdlib functions that must take functions that take a single argument, - but you want to pass an extra parameter to that function. - - TODO0 get working - */ - { - /* - std::vector v = {2, 0, 1}; - assert(std::find_if ( - v.begin(), - v.end(), - std::bind2nd([](int i, int param){return i == param + 1;}, 1) - ) == v.begin()); - */ - } - } -#endif - - /* - # hash - - - - The stdlib furnishes overloaded hash functions for stdlib containers. - - Those functions are implemented as callable classes that implement `()`. - - For base types, those hashes are found under the `functional`. - - For std::vectors, only `std::vector` has a template. - - For other types, they are found in the same header that defines those types: - ex: hash for std::vectors is under ``. - - Returns a `size_t` result. - */ - { - std::cout << "hash" << std::endl; - std::cout << " 1 = " << std::hash()(1) << std::endl; - std::cout << " string abc = " << std::hash()("abc") << std::endl; - } - -#if __cplusplus >= 201103L - /* - # regex - - Finally they are supported! - - Many types are supported: Javascript, grep, awk, ... - - It is probably saner and more powerful to stick to Javascript regexes. - */ - { - // Js has `[:d:]` equivalent POSIX `[:digit:]`. - std::regex r("a.(c|d)"); - } -#endif } -#if __cplusplus >= 201103L - /* - # Attributes - - C++11 introduces a generalized attribute syntax. - - Similar syntaxes have been rmplemented as extensions for a long time in GCC via `__attribtes__` - and in Microsoft with `#pragma`. Now some have been standardized! - - Intended only for functions which don't change behaviour: only to help - compilers optimize or geneate better error messages. - - http://www.stroustrup.com/C++11FAQ.html#attributes - - Attributes can be defined for various objects, and there are 2 standard ones: - `noreturn` and `carries_dependency` - - C11 also has some attributes like `_Noreturn`, but no generalized syntax. - */ - { - try { - noreturn_func(); - } catch (int i) {} - } -#endif - /* # main return diff --git a/cpp/map.cpp b/cpp/map.cpp new file mode 100644 index 0000000..0cd8d16 --- /dev/null +++ b/cpp/map.cpp @@ -0,0 +1,189 @@ +/* +# map + + http://www.cplusplus.com/reference/map/map/ + + Also comes in an unordered version `unordered_map`. + + Ordered. + + Also comes in an multiple value input version `multimap`. + + Does not require a hash function. Usually implemented as a self balancing tree such as a rb tree. + +# unordered_map + + TODO complexity comparison to map. + +# hashmap + + There seems to be no explicit hashmap container, only a generic map interface, + + See map. + + Nonstandard `hash_map` already provided with gcc and msvc++. + It is placed in the `std::` namespace, but it is *not* ISO. +*/ + +#include "common.hpp" + +template +std::string map_to_str(std::map map) { + std::stringstream result; + for (auto& pair : map) { + result << pair.first << ":" << pair.second << ", "; + } + return result.str(); +} + +int main() { + /* + The initializer list constructor makes things very easy. + */ + { + std::map m{ + {0, "zero"}, + {1, "one"}, + {2, "two"}, + }; + } + + /* + emplace + + put a value pair into the map without creating the pair explicitly + + needs gcc 4.8: + */ + { + //std::map m; + //m.emplace(0, "zero"); + //m.emplace(1, "one"); + //m.emplace(2, "two"); + } + + /* + # insert + + Insert pair into map. + + The return value is similar to that of a set insertion with respec to the key. + */ + { + std::map m; + std::pair::iterator,bool> ret; + + ret = m.insert(std::pair(0, "zero")); + assert(ret.first == m.find(0)); + assert(ret.second == true); + + ret = m.insert(std::pair(1, "one")); + assert(ret.first == m.find(1)); + assert(ret.second == true); + + //key already present + ret = m.insert(std::pair(1, "one2")); + assert(m[1] == "one"); + assert(ret.first == m.find(1)); + assert(ret.second == false); + } + + /* + iterate + + Map is ordered. + + It is iterated in key `<` order. + + Iteration returns key value pairs. + */ + { + std::map m{ + {1, "one"}, + {0, "zero"}, + }; + + int i = 0; + int is[] = {0, 1}; + for (auto& im : m) { + assert(im.first == is[i]); + //cout << im->second << endl; + ++i; + } + assert(i == 2); + assert(map_to_str(m) == "0:zero, 1:one, "); + } + + /* + [] operator + + get value from a given key + + WARNING: if the key does not exist, it is inserted with a value with default constructor. + + This can be avoided by using `find` instead of `[]`. + */ + { + std::map m{ + {0, "zero"}, + {1, "one"}, + }; + + assert(m[0] == "zero"); + assert(m[1] == "one"); + + //inserts `(3,"")` because `""` is the value for the default string constructor + assert(m[2] == ""); + assert(m.size() == 3); + } + + /* + # find #check if in map + + Similar to `std::set` find with respect to the keys: + returns an iterator pointing to the pair which has given key, not the value. + + If not found, returns `map::end()` + + This is perferrable to `[]` since it does not insert non-existent elements. + */ + { + std::map m{ + {0, "zero"}, + {1, "one"}, + }; + + assert(m.find(0)->second == "zero"); + assert(m.find(1)->second == "one"); + + assert(m.find(2) == m.end()); + assert(m.size() == 2); + } + + /* + erase + + Remove element from map. + + Returns number of elements removed. + */ + { + int ret; + + std::map m{ + {0, "zero"}, + {1, "one"}, + }; + + std::map m2; + m2.insert(std::pair(0, "zero")); + + ret = m.erase(1); + assert(ret = 1); + + assert(m == m2); + + ret = m.erase(1); + assert(ret == 0); + } +} diff --git a/cpp/mutex.cpp b/cpp/mutex.cpp index 93dd455..5a3894d 100644 --- a/cpp/mutex.cpp +++ b/cpp/mutex.cpp @@ -11,8 +11,6 @@ #include "common.hpp" #if __cplusplus >= 201103L -const int NUM_THREADS = 1000; -const int NUM_ITERS = 1000; long int global = 0; std::mutex mutex; @@ -34,15 +32,13 @@ int main() { #if __cplusplus >= 201103L std::thread threads[NUM_THREADS]; int i; - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) threads[i] = std::thread(threadMain); - } - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) threads[i].join(); + if (global != NUM_OPS) { + printf("fraction of errors = %f\n", (1.0 - (double)global/NUM_OPS)); + assert(false); } - - assert(global == NUM_THREADS * NUM_ITERS); #endif } diff --git a/cpp/operator_overload.cpp b/cpp/operator_overload.cpp index dad5abf..e363d03 100644 --- a/cpp/operator_overload.cpp +++ b/cpp/operator_overload.cpp @@ -105,15 +105,21 @@ class OperatorOverload { /* operator++ - Post and pre increment are both impemented via this operator. - - + - http://stackoverflow.com/questions/3846296/how-to-overload-the-operator-in-two-different-ways-for-postfix-a-and-prefix + - http://stackoverflow.com/questions/6375697/do-i-have-to-return-a-reference-to-the-object-when-overloading-a-pre-increment-o + - http://stackoverflow.com/questions/3574831/why-does-the-postfix-increment-operator-take-a-dummy-parameter */ const OperatorOverload& operator++() { this->i++; return *this; } + // TODO this is wrong. + const OperatorOverload& operator++(int) { + this->i++; + return *this; + } + /* Ambiguous call. @@ -291,45 +297,48 @@ Operator overload and templates T operator/(const T& i, const T& j) {return i + j;} int main() { - //OperatorOverload overload `+` + // OperatorOverload overload `+` { - //== + // == assert(OperatorOverload(3) == OperatorOverload(3)); - //< + // < assert(OperatorOverload(1) < OperatorOverload(2)); - //= + // = { OperatorOverload i(1); assert(i == OperatorOverload(1)); } - //+= + // += { OperatorOverload i(1); i += OperatorOverload(2); assert(i == OperatorOverload(3)); } - //+ + // + assert(OperatorOverload(1) + OperatorOverload(2) == OperatorOverload(3)); - //++ + // ++ { - OperatorOverload i(1); - assert(++i == OperatorOverload(2)); - assert(i == OperatorOverload(2)); + // Prefix + { + OperatorOverload i(1); + assert(++i == OperatorOverload(2)); + assert(i == OperatorOverload(2)); + } - /* TODO understand and get working */ - /* - i = OperatorOverload(1); - assert(i++ == OperatorOverload(1)); - assert(i == OperatorOverload(2)); - */ + // Postfix. TODO + { + OperatorOverload i(1); + //assert(i++ == OperatorOverload(1)); + //assert(i == OperatorOverload(2)); + } } - //- + // - { // Unary assert(-OperatorOverload(1) == OperatorOverload(-1)); @@ -338,7 +347,7 @@ int main() { assert(OperatorOverload(2) - OperatorOverload(1) == OperatorOverload(1)); } - //* + // * { // Dereference assert(*(OperatorOverload(1)) == 1); diff --git a/cpp/preprocessor.cpp b/cpp/preprocessor.cpp index 43ae1d3..22dce27 100644 --- a/cpp/preprocessor.cpp +++ b/cpp/preprocessor.cpp @@ -30,8 +30,14 @@ int main() { */ { #ifdef __cplusplus - printf("__cplusplus = %li\n", __cplusplus); + std::cout << "__cplusplus = " << __cplusplus << std::endl; #endif } + +#ifdef __STDCPP_THREADS__ + // TODO why not defined even though I do have multithreading? + std::cout << "__STDCPP_THREADS__" << __cplusplus << std::endl; + assert(__STDCPP_THREADS__ == 1); +#endif } } diff --git a/cpp/regex.cpp b/cpp/regex.cpp new file mode 100644 index 0000000..cc263df --- /dev/null +++ b/cpp/regex.cpp @@ -0,0 +1,18 @@ +/* +# regex + + Finally they are supported! + + Many types are supported: Javascript, grep, awk, ... + + It is probably saner and more powerful to stick to Javascript regexes. +*/ + +#include "common.hpp" + +int main() { +#if __cplusplus >= 201103L + // Js has `[:d:]` equivalent POSIX `[:digit:]`. + std::regex r("a.(c|d)"); +#endif +} diff --git a/cpp/rtti.md b/cpp/rtti.md new file mode 100644 index 0000000..ddaa332 --- /dev/null +++ b/cpp/rtti.md @@ -0,0 +1,12 @@ +# RTTI + +Run time type information. + +Any function that gets class information explicitly at runtime: + +- `typeid` +- `dynamic_cast` + +Google style 3.26 discourages this, since if you really need it your design is probably flawed. + +Also using `typeid` on variables means that extra meta data must be kept about those variables. diff --git a/cpp/set.cpp b/cpp/set.cpp new file mode 100644 index 0000000..3fbb57f --- /dev/null +++ b/cpp/set.cpp @@ -0,0 +1,151 @@ +/* +# set + + - unique elements: inserting twice does nothing + + - always ordered: $O(log)$ find / insert + + - immutable elements: it is not possible to modify an object, + one must first remove it and resinsert. + + This is so because modification may mean reordering. +*/ + +#include "common.hpp" + +int main() { + // C++11 initializer list + { + { + std::set s{1, 2, 0, 1}; + std::set s2{0, 1, 2}; + assert(s == s2); + } + + { + std::set s = {"a", "c", "b", "a"}; + std::set s1 = {"a","b", "c"}; + assert(s == s1); + } + } + + // You can modify objects if you store pointers. + { + int i = 0; + std::set s; + s.insert(&i); + std::set::iterator it = s.find(&i); + *(*it) = 1; + assert(i == 1); + } + + /* + # insert + + Like for std::vector, insert makes copies. + + Return is a pair conatining: + + - if the item was not present, an iterator to the item inserted and true + - if the item was present, an iterator to the existing item inserted and false + */ + { + std::pair::iterator,bool> ret; + std::set s; + + ret = s.insert(1); + assert(ret.first == s.find(1)); + assert(ret.second == true); + + ret = s.insert(2); + assert(ret.first == s.find(2)); + assert(ret.second == true); + + ret = s.insert(0); + assert(ret.first == s.find(0)); + assert(ret.second == true); + + //item already present: + //nothing is done and returns false on the pair + ret = s.insert(1); + assert(ret.first == s.find(1)); + assert(ret.second == false); + + std::set s1 = {0, 1, 2}; + assert(s == s1); + } + + /* + # erase + + Remove element from set. + + Returns number of elements removed. + */ + { + std::set s = {0, 1, 2}; + + assert(s.erase(1) == 1); + std::set s2 = {0, 2}; + assert(s == s2); + + assert(s.erase(1) == 0); + } + + // ERROR no random access since it uses bidirection iterator. + { + //cout << s[0] << endl; + } + + //size + { + std::set s; + assert(s.size() == 0); + s.insert(0); + assert(s.size() == 1); + } + + /* + iterate + + Biderectional iterator. + + Always sorted. + */ + + /* + find + + If found, returns an iterator pointing to the element. + Else, returns `map::end()` + + find is `log n` time since the container is ordered. + + log n time complexity since always sorted + */ + { + std::set s = {0, 1, 2}; + std::set::iterator it; + + it = s.find(1); + assert(*it == 1); + + it = s.find(3); + assert(it == s.end()); + } + + /* + count + + Count how many times an item is in the set. + + Can only return 1 or 0. + + Equivalent to doing a find. + */ + { + std::set s = {1, 2, 0, 1}; + assert(s.count(1) == 1); + assert(s.count(3) == 0); + } +} diff --git a/cpp/standard_library.md b/cpp/standard_library.md index 22cec96..40369a4 100644 --- a/cpp/standard_library.md +++ b/cpp/standard_library.md @@ -18,6 +18,36 @@ Write `stdlib` and say "Standard Library" instead. GCC comes with an implementation of libstdc++. +It is the main Linux implementation. + +Shared object name: `libstdc++.so`. + +Website: + +Get source code: seems to be on the same tree as gcc? + + git clone git://gcc.gnu.org/git/gcc.git + +Find the shared library: + + locate libstdc++ + +Common location on Linux: + + /usr/lib/ARCH-linux-gnu/libstdc++.so.X + +Locate the headers + + locate /iostream + +Common location: + + /usr/include/c++/4.X/`. + +The Ubuntu package is called `libstdc++6.X`. `dpkg -l | grep libstd`. + +With `g++` the C++ standard library is linked against automatically. This does not happen when compiling with `gcc`, and is one of the many reasons why you should use `g++` whenever compiling C++ instead of `gcc`. + ### Apache C++ Standard Library Dead. diff --git a/cpp/static_assert.cpp b/cpp/static_assert.cpp new file mode 100644 index 0000000..ec2b308 --- /dev/null +++ b/cpp/static_assert.cpp @@ -0,0 +1,28 @@ +/* +# static_assert + + Make assertions at compile time. + + In this way you don't waste time compiling large programs, + or do potentially dangerous runtime operations to test your program. + + Probably became possible on C++11 because of features such as `constexpr`, + which allow to better manage compile time constantness. + + +*/ + +#include "common.hpp" + +int main() { +#if __cplusplus >= 201103L + static_assert(0 < 1, "msg"); + + // ERROR: static assertion failed + //static_assert(0 > 1, "msg"); + + std::srand(time(NULL)); + // ERROR: needs to be a constexpr + //static_assert(std::rand() >= 0); +#endif +} diff --git a/cpp/string.cpp b/cpp/string.cpp new file mode 100644 index 0000000..7752e80 --- /dev/null +++ b/cpp/string.cpp @@ -0,0 +1,310 @@ +/* +# string +*/ + +#include "common.hpp" + +std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} + +int main() { + // Initialize from string literal. + { + std::string s = "abc"; + } + + // cout works as expected. + { + std::string s = "abc"; + std::stringstream oss; + oss << s; + assert(oss.str() == "abc"); + } + + /* + # + for strings + + # cat + + # concatenate. + + Creates a new string. + + The only way to do inline this without creating a new string seems to be by using stringstream. + http://stackoverflow.com/questions/662918/how-do-i-concatenate-multiple-c-strings-on-one-line + */ + { + std::string s = "ab"; + std::string s1 = "cd"; + assert(s + s1 == "abcd"); + assert(s + "cd" == "abcd"); + assert("cd" + s == "cdab"); + } + + // length + { + std::string s = "abc"; + assert(s.length() == 3); + } + + { + std::string s = "abc"; + s[0] = 'A'; + assert(s == "Abc"); + + // BAD: no born check! Compiles. + //s[3] = 'd'; + } + + /* + # lowercase + + http://stackoverflow.com/questions/313970/stl-string-to-lower-case + */ + { + // Best stdlib way with transform: + std::string s = "AbCd1_"; + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + assert(s == "abcd1_"); + + //Boost has a single function: boost::algorithm::to_lower(str); + } + + /* + # c_str + + Convert std::string to C null terminated char* string. + */ + { + std::string s = "abc"; + assert((std::strcmp(s.c_str(), "abc")) == 0); + } + + // # substring + { + std::string s = "abcde"; + assert(s.substr(1, 3) == "bcd"); + } + + // # Split at a character into array of strings. + { + // Best stdlib solution for any character: http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c + // There are shorters sstream solutions that split at whitespace. + // For Boost it's a one liner. + { + assert((split("01:23::45", ':') == std::vector{"01", "23", "", "45"})); + + std::vector v; + split("01:23::45", ':', v); + assert((v == std::vector{"01", "23", "", "45"})); + } + } + + /* + # strip + + # chomp + + # trim + + Exact same techniques as removing elements from vectors but for characters. + + It's just that those operations are so common on strings... + + http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring + */ + { + // A single character: remove and erase idiom. + // Single remove_all call in Boost. + { + std::string s = "a bc d"; + auto end = s.end(); + s.erase(std::remove(s.begin(), end, ' '), end); + assert((s == "abcd")); + } + + // Any character in a string: remove_if + custom function. std::ispunct is a typical choice. + // Single liner with boost::remove_if + is_any_of. + { + std::string s = "a,bc. d"; + auto end = s.end(); + // stdc ispunct: + s.erase(std::remove_if(s.begin(), end, ::ispunct), end); + // stdlib ispunct. Fails without the cast. + //s.erase(std::remove_if(s.begin(), end, (int(*)(int))std::ispunct), end); + assert((s == "abc d")); + } + } + + /* + # getline + + Read istream until a any given character, by default newline, and store chars read into a string. + + The other major method of getting data from streams is `operator<<`, + which generaly speaking reads until whitespace. getline is generaly saner. + + Returns the stream itself, which allows to: + + - chain calls + - do while(getline) combos, as streams can be converted to bool via the `void*()` + operator which returns a pointer type which is then converted to a boolean. + */ + { + // Up to newline. + { + std::stringstream ss; + std::string s; + ss << "ab\n\nc"; + + // The delim is removed from the string. + assert(std::getline(ss, s)); + assert(s == "ab"); + + // Empty + assert(std::getline(ss, s)); + assert(s == ""); + + // No problem if end of stream. + assert(std::getline(ss, s)); + assert(s == "c"); + + // Stream over. + assert(!std::getline(ss, s)); + } + + // The stream itself is returned. + { + std::stringstream ss; + std::string s; + std::string s2; + ss << "ab\n\nc"; + std::getline(std::getline(ss, s), s2); + assert(s == "ab"); + assert(s2 == ""); + } + + // Up to custom char. + { + std::stringstream ss; + std::string s; + ss << "ab::f"; + std::getline(ss, s, ':'); + assert(s == "ab"); + } + + // Read stream line-wise. + { + std::stringstream ss; + std::string line; + std::vector lines; + ss << "ab\n\nc"; + while (getline(ss, line)) + lines.push_back(line); + assert((lines == std::vector{"ab", "", "c"})); + } + } + + /* + # stringstream + + # basic_stringstream + + An iostream String backed implementation. + + The following is defined: + + typedef basic_stringstream stringstream; + + typedef basic_stringstream + + Very useful to test streams without creating files / stdin. + */ + { + std::stringstream oss("abcd"); + + // str does not clear the std::stringstream object + assert(oss.str() == "abcd"); + + // To clear it you could do: http://stackoverflow.com/questions/20731/in-c-how-do-you-clear-a-stringstream-variable + // Set to empty: + oss.str(""); + // Clear flags. Very important, not only for error indicators but also for end of stream. + oss.clear(); + assert(oss.str() == ""); + + // ERROR: use of deleted function because the constructor is =delete. + //oss = std::stringstream(); + } + + // Possible application: build up a huge string step by step. + // May be more efficient than concatenations which always generates new objects. + { + std::stringstream oss; + oss << "ab"; + oss << "cd"; + assert(oss.str() == "abcd"); + } + + /* + # int to string + + There are a few standard alternatives. + + + */ + { + /* + C++11 solves the question once and for all with a robust one-liner for base types. + + It is not intended however for class input. + */ +#if __cplusplus >= 201103L + assert(std::to_string(123) == "123"); +#endif + + /* + std::stringstream seems to be the best pre C++11 solution. + + It also has the advantage of working for any class that implements `operator<<`. + */ + { + std::stringstream oss; + oss << 123; + assert(oss.str() == "123"); + } + + /* + C sprintf + + Works, but uses too many conversion operations. + */ + { + char cs[16]; + std::sprintf(cs, "%d", 123); + std::string s = (cs); + assert(s == "123"); + } + } + + // #int to string + // http://stackoverflow.com/questions/7663709/convert-string-to-int-c + { + // Best C++11 error checking option: stoi +#if __cplusplus >= 201103L + assert(std::stoi("123") == 123); +#endif + } +} diff --git a/cpp/struct.cpp b/cpp/struct.cpp new file mode 100644 index 0000000..5498d26 --- /dev/null +++ b/cpp/struct.cpp @@ -0,0 +1,42 @@ +/* +# struct + + Structs in C++ are very similar to classes: support access modifiers, + inheritance, constructors, templates, etc. + + The major difference between them is that the default access modifier for structs + is public, while for classes it is private. + + This is why structs are used on many simple short language examples: + no public line is needed. + + The Google C++ style guide recommends using struct only if there is no constructors, + and classes otherwise. + + +*/ + +#include "common.hpp" + +template +struct BaseStruct { + T i; + BaseStruct(T i) : i(i) {} + + protected: + int iProtected; + + private: + int iPrivate; +}; + +struct DerivedStruct : BaseStruct { + DerivedStruct(int i) : BaseStruct(i) { + iProtected = i; + } +}; + +int main() { + struct DerivedStruct s(1); + assert(s.i == 1); +} diff --git a/cpp/thread.cpp b/cpp/thread.cpp index 8a9b80c..c396b27 100644 --- a/cpp/thread.cpp +++ b/cpp/thread.cpp @@ -1,7 +1,5 @@ /* -TODO this example is too complex, split it up into smaller examples. - -# Thread +# thread # Multithreading @@ -14,72 +12,22 @@ TODO this example is too complex, split it up into smaller examples. #include "common.hpp" -int nNsecs = 10; -int threadGlobal = 0; -int threadGlobalMutexed = 0; -std::mutex threadGlobalMutex; -std::thread::id lastThreadId; -std::set threadIds; - -int threadGlobalEq0 = 0; -int threadGlobalMutexedEq0 = 0; -int threadChange = 0; - -void threadMain(int threadCountToSqrt) { - std::thread::id id = std::this_thread::get_id(); - for (int i = 0; i < threadCountToSqrt; i++) { - for (int j = 0; j= 201103L + /* + # tuple + + Hold a ordered collection of elements. + + Each element can be of a different type. + + The length is always fixed. + */ + { + //create + { + //constructor + { + std::tuple t0(0, 'a', "a"); + } + + /* + # make_tuple + + forwards arguments to tuple constructor. + + The advantage over the constructor is that since it is a function + template argument deduction can be done, so we don't need to type in + template arguments. + + Remember that template argument deduction cannot be done for constructors. + */ + { + std::tuple t; + + //without make_tuple + t = std::make_tuple(0, 'a', "a"); + t = std::tuple(0, 'a', "a"); + + //with make_tuple + } + + //tuple from pair + { + std::tuple t2( std::pair(0, 'a')); + } + + //uniform initialization + { + std::tuple t{0, 'a', "a"}; + } + + // Fails because the tuple constructor are is `explicit`! + // TODO Rationale? + { + //std::tuple t = {0, 1}; + //std::tuple t[]{{0, 1}}; + } + } + + /* + # get + + Get single element from tuple. + + Returns references, so it is possible to modify the tuples with them. + + Copies are made from input elements + */ + { + std::tuple t0(0, "abc"); + + assert(std::get<0>(t0) == 0); + assert(std::get<1>(t0) == "abc"); + + std::get<0>(t0) = 1; + assert(std::get<0>(t0) == 1); + + std::get<1>(t0)[0] = '0'; + assert(std::get<1>(t0) == "0bc"); + } + + /* + # tie + + Unpack a tuple. + + Unpack by reference seems not to be possible: + + # ignore + + Magic that exists only to ignore one of tie outputs. + */ + { + int i; + std::string s; + std::tuple t(1, 1.5, "abc"); + std::tie(i, std::ignore, s) = t; + assert(i == 1); + assert(s == "abc"); + + // Clearly copies are made. + i = 2; + assert(std::get<0>(t) == 1); + } + + /* + Relational operators operations are implemented + + + + `<` family is lexicographical. + */ + { + std::tuple t0(0, 'a'); + std::tuple t1(0, 'a'); + std::tuple t2(1, 'b'); + std::tuple t3(-1, 'b'); + std::tuple t4(0, 'b'); + + assert(t0 == t1); + assert(t0 != t2); + assert(t0 < t2); + assert(t0 > t3); //-1 counts + assert(t0 < t4); //0 ties, 'a' < 'b' + } + + //swap contents of two tuples of same type + { + std::tuple t0(0, 'a'); + std::tuple t1(1, 'b'); + + std::tuple old_t0 = t0; + std::tuple old_t1 = t1; + + t0.swap(t1); + + assert(t0 == old_t1); + assert(t1 == old_t0); + } + } +#endif + + /* + # pair + + Particular case of tuple for two elements + + Methods which also exist for tuple will not be discussed. + + Specially important because of `map`. + */ + { + //access: can also be done via `.first` and `.second` in addition to tuple `get`. + { + std::pair p(0, 'a'); + assert(std::get<0>(p) == p.first); + assert(std::get<1>(p) == p.second); + } + } + + /* + # forward + + TODO + */ + { + } +} diff --git a/cpp/valarray.cpp b/cpp/valarray.cpp new file mode 100644 index 0000000..2f768f6 --- /dev/null +++ b/cpp/valarray.cpp @@ -0,0 +1,61 @@ +/* +# valarray + + Array of values. Wors much like a mathematical vector. + + Container that overloads many mathematical operations in a similar way to what Fortran does, + which may be more efficient and convenient. + + Very obscure, for several reasons: + + - other techniques achieve what it achieves + - low compiler support + + Downsides compared to vectors: + + - not resizable, so no push_back + + +*/ + +#include "common.hpp" + +int main() { + std::valarray v; + std::valarray v0{0, 1, 2}; + std::valarray v1{3, 4, 5}; + + assert(v0.sum() == 3); + assert(v0.min() == 0); + assert(v0.max() == 2); + + /* + v = v0; + v.cshift(1); + assert((v == std::valarray{1, 2, 0}).min()); + */ + + // == is elementwise equality. + // + // For equality of all elements, do `.min() == true` + { + std::valarray v0{0, 1, 2}; + std::valarray v1{0, 2, 2}; + assert(((v0 == v1) == std::valarray{true, false, true}).min()); + + } + + // +, -, *, /, etc are overloaded elementwise. + // + // They are also overloaded for contained data type. + { + assert((v0 + v1 == std::valarray{3, 5, 7}).min()); + assert((v0 + 1 == std::valarray{1, 2, 3}).min()); + } + + // Basic cmath functions are overloaded for valarray elementwise. + { + assert((abs(std::valarray{-1, 0, 1}) == std::valarray{1, 0, 1}).min()); + assert((pow(std::valarray{-2, 0, 2}, 2) == std::valarray{4, 0, 4}).min()); + } +} diff --git a/cpp/vector.cpp b/cpp/vector.cpp new file mode 100644 index 0000000..5932adc --- /dev/null +++ b/cpp/vector.cpp @@ -0,0 +1,528 @@ +/* +# vector + + Array backed conatiner that grows / shrinks as necessary. + + $O(1)$ random access. + + $O(n)$ element removal from interior + + $O(1)$ element append to end (amortized, $O(n)$ worst case) + + All methods that work for several SLT containers shall only be cheated here once. +*/ + +#include "common.hpp" + +int main() { + // Create + { + // Empty + { + std::vector v; + // C++11 initializer lists: + std::vector v1{}; + assert(v == v1); + } + + /* + Fill constructor. + + Make a `std::vector` with n copies of a single value. + */ + { + //copies of given object + { + assert(std::vector(3, 2) == std::vector({2, 2, 2})); + } + + //default constructed objects. int = 0. + { + assert(std::vector(3) == std::vector({0, 0, 0})); + } + } + + // Range copy. + { + std::vector v{0, 1, 2}; + std::vector v1(v.begin(), v.end()); + assert(v == v1); + } + + // From existing array. + { + int myints[]{0, 1, 2}; + std::vector v(myints, myints + sizeof(myints) / sizeof(int)); + std::vector v1 = {0, 1, 2}; + assert(v == v1); + } + } + + // Vectors have order. + { + std::vector v{0, 1, 2}; + std::vector v1{2, 1, 0}; + assert(v != v1); + } + + /* + # Contigous storage + + # Data + + Storage is required to be contiguous by TR1: + http://stackoverflow.com/questions/849168/are-stdvector-elements-guaranteed-to-be-contiguous + + C++11 introduces the `data()` method which returns a pointer to the first element. + It works even if the vector is empty. + http://stackoverflow.com/questions/6485496/how-to-get-stdvector-pointer-to-the-raw-data + + Before C++11, `&v[0]` works for non-empty vectors. + + `vector` as usual is an exception. + */ + { + std::vector v{0, 1, 2}; + assert(&v[0] == v.data()); + // True because contiguous: + assert(v.data()[1] == v[1]); + } + + // size methods + { + /* + # size + + # length of vector + + # size_type + + Number of elements in std::vector. + + This has type std::vector::size_type + */ + { + std::vector v; + assert(v.size() == 0); + v.push_back(0); + assert(v.size() == 1); + } + + /* + # resize + + If larger than current size, append given element at end. + + If smaller than current size, remove elements from end. + */ + { + // Reduce size + { + std::vector v{0, 1}; + v.resize(1); + assert((v == std::vector{0})); + } + + // Increase size + { + + // Using default constructor objects. + { + std::vector v{1}; + v.resize(3); + assert((v == std::vector{1, 0, 0})); + } + + // Using copies of given object. + { + std::vector v{1}; + v.resize(3, 2); + assert((v == std::vector{1, 2, 2})); + } + } + } + } + + // Capacity methods. + { + /* + # capacity + + Get currently allocated size. + + Different from size, which is the number of elements in the std::vector! + + At least as large as size. + + Likely to be a power of 2 on most implementations. + */ + { + std::vector v; + v.push_back(0); + v.push_back(1); + v.push_back(2); + assert(v.capacity() >= 3); + std::cout << "capacity = " << v.capacity() << std::endl; + } + + // # max_size: estimative of what your OS allows you to allocate + { + std::cout << "max_size (MiB) = " << std::vector().max_size() / (1 << 20) << std::endl; + } + + // # reserve: increase allocated size if larger than current size. + { + std::vector v; + v.reserve(3); + assert(v.capacity() >= 3); + // size() is untouched + assert(v.empty()); + } + +#if __cplusplus >= 201103L + // # shrink_to_fit + { + std::vector v{0, 1}; + v.reserve(4); + v.shrink_to_fit(); + assert(v.capacity() == 2); + } +#endif + } + + // `std::vector` stores copies of elements, not references. + { + std::string s = "abc"; + std::vector v{s}; + v[0][0] = '0'; + assert(v[0] == "0bc"); + assert(s == "abc"); + } + + // Modify. + { + { + std::vector v; + v = {0}; + v = {0, 1}; + assert((v == std::vector{0, 1})); + } + + /* + # push_back + + # append + + Push to the end of the std::vector. + + Amortized time O(1), but may ocassionaly make the std::vector grow, + which may required a full data copy to a new location if the + current backing array cannot grow. + + # push_front + + Does not exist for std::vector, as it would always be too costly (requires to move + each element forward.) Use deque if you need that. + */ + { + std::vector v; + std::vector v1; + + v.push_back(0); + v1 = {0}; + assert(v == v1); + + v.push_back(1); + v1 = {0, 1}; + assert(v == v1); + + /* + push_back makes copies with assign `=` + + If you want references, use pointers, or even better, auto_ptr. + */ + { + std::vector v; + std::string s = "abc"; + + v.push_back(s); + v[0][0] = '0'; + assert(v[0] == "0bc"); + + //s was not changed + assert(s == "abc"); + } + } + + /* + # pop_back + + Remove last element from std::vector. + + No return val. Rationale: + */ + { + std::vector v{0, 1}; + + v.pop_back(); + assert(v == std::vector{0}); + + v.pop_back(); + assert(v == std::vector{}); + } + + /* + # insert + + This operation is inneficient for `std::vector` if it is not done at the end. + + # concatenate + + The range form of insert can be used to append one vector to anoter. + */ + { + // Single element form. + { + std::vector v = {0,1}; + std::vector v1; + + v.insert(v.begin(), -1); + v1 = {-1, 0, 1}; + assert(v == v1); + + v.insert(v.end(), 2); + v1 = {-1, 0, 1, 2}; + assert(v == v1); + } + + // Range form. + { + std::vector v = {0,1}; + std::vector v1 = {2,3}; + + v.insert(v.end(), v1.begin(), v1.end()); + assert((v == std::vector{0, 1, 2, 3})); + } + } + + /* + # erase + + Remove given elements from container given iterators to those elements. + + This operation is inneficient for std::vectors, + since it may mean reallocation and therefore up to $O(n)$ operations. + + Returns a pointer to the new location of the element next to the last removed element. + */ + { + // Single element + { + std::vector v{0, 1, 2}; + auto it = v.erase(v.begin() + 1); + assert((v == std::vector{0, 2})); + assert(*it == 2); + } + + // Range + { + std::vector v{0, 1, 2, 3}; + auto it = v.erase(v.begin() + 1, v.end() - 1); + assert((v == std::vector{0, 3})); + assert(*it == 3); + } + } + + /* + # remove + + Helper to remove all elements that compare equal to a value from container. + + Does not actually remove the elements: only ensures that the beginning of the range + does not contain the item to be removed. + + Ex: + + 0, 1, 0, 2, 0, 1 + + Value to remove: `0` + + Range to remove from: + + 0, 1, 0, 2, 0, 1 + ---------- + + After the remove: + + 1, 2, X, Y, 0, 1 + ---------- + + where `X` and `Y` are trash, and not necessarily 0! + + To actually remove the items, an `erase` is needed after remove + because `remove` is not a class method and thus cannot remove items from a container. + + This is called the erase and remove idiom. + + After a remove the container becomes: + + 1, 2, 0, 1 + + # erase and remove idiom + + # remove and erase idiom + + See remove. + */ + { + // Verbose version + { + std::vector v{0, 1, 0, 2, 0, 1}; + auto end = std::next(v.end(), -2); + auto firstTrashIt = std::remove(v.begin(), end, 0); + // Unpredictable result: + std::cout << "remove:"; + for (auto& i : v) std::cout << " " << i; + std::cout << std::endl; + v.erase(firstTrashIt, end); + assert((v == std::vector{1, 2, 0, 1})); + } + + // Compact version + { + std::vector v{0, 1, 0, 2, 0, 1}; + auto end = std::next(v.end(), -2); + v.erase(std::remove(v.begin(), end, 0), end); + assert((v == std::vector{1, 2, 0, 1})); + } + } + + // # remove_if # filter + // Algorithm. Remove if a given function evaluates to true on an element. + { + std::vector v{0, 1, 2, 3, 4}; + auto end = v.end(); + v.erase(std::remove_if(v.begin(), end, odd), end); + assert((v == std::vector{0, 2, 4})); + + // Common combo with lambdas + { + std::vector v{0, 1, 2, 3, 4}; + auto end = v.end(); + v.erase(std::remove_if(v.begin(), end, + [](int i) {return i % 2 == 1;}), end); + assert((v == std::vector{0, 2, 4})); + } + } + + // # transform + // Algorithm. Replace elements by output of a function. + { + std::vector v{0, 1, 2}; + std::transform(v.begin(), v.end(), v.begin(), + [](int i) {return i * i;}); + assert((v == std::vector{0, 1, 4})); + } + + // # clear + { + std::vector v{0, 1, 2}; + v.clear(); + assert(v.size() == 0); + } + + /* + # print vector + + # vector to string + + No built-in way. + + http://stackoverflow.com/questions/4850473/pretty-print-c-stl-containers + 190 votes on question, 30 on top answer! Come on C++! + + http://stackoverflow.com/questions/1430757/c-vector-to-string?lq=1 + */ + // ERROR: no default operator `<<`. + //cout << v; + } + + // Random access is O(1) since array backed + { + + std::vector v{0, 1, 2}; + + // First element: + + assert(v.front() == 0); + assert(v.front() == v[0]); + + // Last element: + + assert(v.back() == 2); + + // Nth element: + + v[0] = 1; + assert(v[0] == 1); + + /* + BAD: just like array overflow will not change std::vector size, + and is unlikelly to give an error + */ + { + //v1[2] = 2; + } + + // #back Get reference to last element in vector. + // #front Get reference to first element in vector. + // #at Like `[]`, but does bound checking and throws `out_of_range` in case of overflow. + { + std::vector v{0, 1, 2}; + assert(v.front() == 0); + assert(v.at(1) == 1); + assert(v.back() == 2); + try { + assert(v.at(3) == 0); + } catch (std::out_of_range& e) { + } catch (...) { + assert(false); + } + } + } + + /* + # bool std::vector + + *bool std::vectors are evil!* + + The standard requires `vector` to have an specialization for bool which packs bits efficiently. + + While efficient, in order to work this specialization breaks common std::vector interfaces + that require taking addresses only in the case of this specialization, since it does not + make sense to takes addresses anymore. + + Alternatives to `vector`: + + A good alternative seem to be deque, which behaves as intended. + */ + { + // Works fine and dandy as expected. + { + std::vector v{1, 0}; + int& i(v[0]); + } + + // Does not compile!!! + { + std::vector v{true, false}; + //bool& b(v[0]); + } + + // It was not a problem with bool, the problem really is `vector`. + { + bool b[]{true, false}; + bool& b2(b[0]); + } + } +} diff --git a/gcc/Makefile_params b/gcc/Makefile_params index f17b300..66eee2d 100644 --- a/gcc/Makefile_params +++ b/gcc/Makefile_params @@ -1,3 +1,2 @@ -# Redefine entire GCC to remove pedantic. PEDANTIC := STD := gnu11 diff --git a/gcc/atomic_fetch_add.c b/gcc/atomic_fetch_add.c new file mode 100644 index 0000000..a61edd0 --- /dev/null +++ b/gcc/atomic_fetch_add.c @@ -0,0 +1,33 @@ +#include +#include +#include + +enum CONSTANTS { + NUM_THREADS = 1000, + NUM_ITERS = 1000 +}; + +int global = 0; + +void* main_thread(void *arg) { + int i; + for (i = 0; i < NUM_ITERS; ++i) { +#ifdef FAIL + global++; +#else + __atomic_fetch_add(&global, 1, __ATOMIC_SEQ_CST); +#endif + } + return NULL; +} + +int main() { + int i; + pthread_t threads[NUM_THREADS]; + for (i = 0; i < NUM_THREADS; ++i) + pthread_create(&threads[i], NULL, main_thread, NULL); + for (i = 0; i < NUM_THREADS; ++i) + pthread_join(threads[i], NULL); + assert(global == NUM_THREADS * NUM_ITERS); + return EXIT_SUCCESS; +} diff --git a/posix/Makefile_params b/posix/Makefile_params deleted file mode 100644 index 4ffc7ee..0000000 --- a/posix/Makefile_params +++ /dev/null @@ -1,2 +0,0 @@ -LIBS := -lm -pthread -D := #-DFAIL diff --git a/posix/interactive/Makefile_params b/posix/interactive/Makefile_params deleted file mode 120000 index f5ab66f..0000000 --- a/posix/interactive/Makefile_params +++ /dev/null @@ -1 +0,0 @@ -../Makefile_params \ No newline at end of file diff --git a/posix/pthread_mutex.c b/posix/pthread_mutex.c index 5cbac84..45ef8a6 100644 --- a/posix/pthread_mutex.c +++ b/posix/pthread_mutex.c @@ -58,16 +58,10 @@ void* main_thread(void *arg) { int main() { pthread_t threads[NUM_THREADS]; int i; - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) pthread_create(&threads[i], NULL, main_thread, NULL); - } - - for (i = 0; i < NUM_THREADS; ++i) { + for (i = 0; i < NUM_THREADS; ++i) pthread_join(threads[i], NULL); - } - assert(global == NUM_THREADS * NUM_ITERS); - return EXIT_SUCCESS; }