Last active
November 18, 2021 16:56
-
-
Save DBJDBJ/554d5d6cdba2dacea5cf61c4796f384f to your computer and use it in GitHub Desktop.
Very simple but light and probably fast 2D array. C++17. Version 6. Types left in here, from std lib are in just for testing code. Used Visual Studio 2019 and clang-cl .
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define DBJ_MTX_TESTING | |
// (c) 2019-2021 by [email protected] | |
// License: CC BY SA 4.0 | |
#include <assert.h> | |
#include <stdlib.h> | |
#include <string.h> // memcpy | |
namespace dbj::mtx { | |
enum class version { major = 6, minor = 1, patch = 0 }; | |
// the limits | |
constexpr auto MAX_STACK_BLOCK = 0xFF; | |
constexpr auto MAX_HEAP_BLOCK = 0xFFFF; | |
// we do not need std::array | |
template< typename T, size_t N > | |
inline auto simple_stack() noexcept { | |
static_assert(N < MAX_STACK_BLOCK, "max stack block is 0xFF"); | |
T buff_[N]{}; | |
return[buff_, size = N](size_t const& idx) mutable->T& { | |
assert(idx < size); | |
return buff_[idx]; | |
}; | |
} | |
// and we do not need std::vector too | |
template< typename T, size_t N > | |
inline auto simple_heap() noexcept | |
{ | |
static_assert(N < MAX_HEAP_BLOCK, "max heap_block_type block is 0xFFFF"); | |
// Q: is this machinery really needed? | |
// A: yes it is, proper copy and move struct with pointer member is the only way | |
// also the destructor in here is the only way this is free'd | |
struct heap_block_type | |
{ | |
size_t size{ N }; | |
// this has to be proper copy/move able struct | |
// seeing bellow compiler does not know the block size | |
T* block{}; | |
heap_block_type() noexcept : block((T*)calloc(N, sizeof(T))) { assert(block); } | |
~heap_block_type() noexcept { if (block) { free(block); block = nullptr; } } | |
void copy(heap_block_type & left, heap_block_type const& right) noexcept { | |
assert(left.block); | |
assert(left.size == right.size); | |
::memcpy(left.block, right.block, left.size); | |
} | |
heap_block_type(heap_block_type const& other_) noexcept : block((T*)calloc(N, sizeof(T))) { | |
copy(*this, other_); | |
} | |
heap_block_type & operator = (heap_block_type const& other_) noexcept { | |
copy(*this, other_); return *this; | |
} | |
void swap(heap_block_type & left, heap_block_type & right) noexcept { | |
using namespace std; | |
left.size = right.size; | |
left.block = right.block; | |
right.block = nullptr; | |
} | |
heap_block_type(heap_block_type&& other_) noexcept { | |
swap(*this, other_); | |
} | |
heap_block_type& operator = (heap_block_type && other_) noexcept { | |
swap(*this, other_); return *this; | |
} | |
}; | |
return[heap_instance_ = heap_block_type{}, size = N](size_t const& idx) mutable->T& { | |
assert(idx < size); | |
return heap_instance_.block[idx]; | |
}; | |
} // simple_heap() | |
template< typename T, size_t rows, size_t cols, typename F > | |
inline constexpr auto mx(F source_) | |
{ | |
// source_ has to be callable | |
// lambda is local class | |
// arry will be a member of that class | |
return[arry = source_()] | |
// and this will be a function call operator on that class | |
(size_t row_, size_t col_) mutable->T& | |
{ | |
assert(row_ < rows); // from here we reach the template args | |
assert(col_ < cols); // of the immediate closure | |
return arry(row_ * rows + col_); | |
}; | |
} // mx() | |
#undef dbj_mx_make | |
#undef dbj_mx_make_heap | |
#undef dbj_mx_make_stack | |
#define dbj_mx_make(T, R, C, K) dbj::mtx::mx<T, R, C>(dbj::mtx::K<T, (R+1) * (C+1)>) | |
#define dbj_mx_make_heap(T,R,C) dbj_mx_make(T, R, C, simple_heap) | |
#define dbj_mx_make_stack(T,R,C) dbj_mx_make(T, R, C, simple_stack) | |
} // dbj::mtx | |
/// | |
/// Testing | |
/// | |
#ifdef DBJ_MTX_TESTING | |
#include <iomanip> // setw | |
#include <sstream> | |
#include <iostream> | |
namespace dbj::mtx { | |
inline auto changer = [](auto matrix_, size_t row_, size_t col_, auto value_) { | |
matrix_(row_, col_) = value_; | |
return matrix_; | |
}; | |
// print to buffer | |
// NOTE: compared to C, this is almost tragi-comical code | |
inline auto | |
printer = [](auto matrix_, size_t width_, size_t height_) | |
-> std::string { | |
using namespace std; | |
std::ostringstream rez_; | |
rez_ << std::boolalpha ; | |
for (size_t row_ = 0; row_ < width_; row_++) { | |
rez_ << "\n"; | |
for (size_t col_ = 0; col_ < height_; col_++) { | |
rez_ << setw(3) << matrix_(row_, col_) << " "; | |
} | |
} | |
rez_ << "\n"; | |
return rez_.str(); | |
}; | |
/// | |
/// log callback signature: | |
/// void (*)( std::string_view ) | |
/// | |
template< | |
typename log_callback, | |
typename value_type , | |
size_t width_ = 3, // 0,1,2 | |
size_t height_ = 3, // 0,1,2 | |
size_t last_col_ = width_ - 1, // 0,1,2 | |
size_t last_row_ = height_ - 1 // 0,1,2 | |
> | |
inline void test_dbj_matrix_creation(log_callback log, value_type const& val_) | |
{ | |
using namespace dbj::mtx; | |
using namespace std; | |
// test move in, move out and the rest | |
auto reporter = [&](auto matrix_, const char* prompt_) { | |
string report = prompt_ + | |
printer(changer(matrix_, last_col_, last_row_, val_), width_, height_); | |
log(report); | |
return matrix_; | |
}; | |
auto stack_mx_ = reporter(dbj_mx_make_stack(value_type, width_, height_), | |
"\n matrix_on_stack \n"); | |
auto heap_mx_ = reporter(dbj_mx_make_heap(value_type, width_, height_), | |
"\n matrix_on_heap \n"); | |
} | |
} // dbj::mtx | |
// log callback signature: void (*)( std::string_view ) | |
void log_(std::string_view sv) | |
{ | |
printf("%s", sv.data()); | |
} | |
int main(void) | |
{ | |
dbj::mtx::test_dbj_matrix_creation(log_, 42); | |
dbj::mtx::test_dbj_matrix_creation(log_, true); | |
return 42; | |
} | |
#endif // DBJ_MTX_TESTING |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please read this https://dbj.org/lambda-powered-matrix/ beforehand