commit f0445542ce1ff644cb40aa0ffd8fcf6cd2e6bf3f Author: szdytom Date: Sun Feb 4 13:47:53 2024 +0800 Add procedure call impl Signed-off-by: szdytom diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8274b1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +build +*.zip + +# Executables +*.pch +*.out +*.exe +*.dll +*.so + +# Node.JS +package-lock.json +node_modules + +# Editor +*.swp +.vscode + +# Xmake +.xmake + diff --git a/processor/pc/BinaryBuffer.cpp b/processor/pc/BinaryBuffer.cpp new file mode 100644 index 0000000..c2cbdca --- /dev/null +++ b/processor/pc/BinaryBuffer.cpp @@ -0,0 +1,36 @@ +#include "BinaryBuffer.h" +#include + +BinaryBuffer::BinaryBuffer() {} + +BinaryBuffer& BinaryBuffer::operator<<(const std::string_view& x) { + std::uint32_t len = x.size(); + *this << len; + for (char c : x) + *this << c; + return *this; +} + +BinaryBuffer& BinaryBuffer::operator>>(std::string& x) { + std::uint32_t len; + *this >> len; + x.resize(len); + for (char& c : x) + *this >> c; + return *this; +} + +std::size_t BinaryBuffer::size() const { + return data.size(); +} + +bool BinaryBuffer::empty() const { + return data.empty(); +} + +std::ostream& operator<<(std::ostream &out, const BinaryBuffer &x) { + for (auto v : x.data) + out.put(v); + return out; +} + diff --git a/processor/pc/BinaryBuffer.h b/processor/pc/BinaryBuffer.h new file mode 100644 index 0000000..a3304f3 --- /dev/null +++ b/processor/pc/BinaryBuffer.h @@ -0,0 +1,128 @@ +#ifndef OG_BINARY_BUFFER_H_ +#define OG_BINARY_BUFFER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +using byte = unsigned char; + +inline namespace { + +template +concept IsTupleLike = requires(T t) { + std::tuple_size::value; +}; + +template +concept IsBasicType = std::integral || std::is_enum::value + || std::same_as || std::same_as; + +template +concept IsTupleConvertable = requires(T t) { + IsTupleLike; +}; + +static_assert(IsTupleLike>); + +} + +class BinaryBuffer { +public: + BinaryBuffer(); + + template + requires IsBasicType + BinaryBuffer& operator>>(T& x) { + if (sizeof(T) <= data.size()) { + auto ptr = reinterpret_cast(&x); + for (std::size_t i = 0; i < sizeof(T); ++i) { + ptr[i] = data.front(); + data.pop_front(); + } + } + return *this; + } + + template + requires IsBasicType + BinaryBuffer& operator<<(const T& x) { + auto ptr = reinterpret_cast(&x); + for (std::size_t i = 0; i < sizeof(T); ++i) { + data.push_back(ptr[i]); + } + return *this; + } + + template + BinaryBuffer& operator<<(const std::vector& x) { + uint32_t len = x.size(); + *this << len; + for (const T& element : x) + *this << element; + return *this; + } + + template + BinaryBuffer& operator>>(std::vector& x) { + uint32_t len; + *this >> len; + x.resize(len); + for (std::uint32_t i = 0; i < len; ++i) + *this >> x[i]; + return *this; + } + + template + requires IsTupleLike + BinaryBuffer& operator<<(const T& x) { + std::apply([this](const auto&... elements) { + (*this << ... << elements); + }, x); + return *this; + } + + template + requires IsTupleLike + BinaryBuffer& operator>>(T& x) { + std::apply([this](auto&... elements) { + (*this >> ... >> elements); + }, x); + return *this; + } + + template + requires IsTupleConvertable + BinaryBuffer& operator<<(const T &x) { + *this << x.asTuple(); + return *this; + } + + template + requires IsTupleConvertable + BinaryBuffer& operator>>(T &x) { + *this >> x.asTuple(); + return *this; + } + + BinaryBuffer& operator<<(const std::string_view &); + BinaryBuffer& operator>>(std::string&); + + std::size_t size() const; + bool empty() const; + + friend std::ostream& operator<<(std::ostream &, const BinaryBuffer &); + +private: + std::deque data; +}; + +std::ostream& operator<<(std::ostream &, const BinaryBuffer &); + +#endif + diff --git a/processor/pc/ProcedureManager.h b/processor/pc/ProcedureManager.h new file mode 100644 index 0000000..f706714 --- /dev/null +++ b/processor/pc/ProcedureManager.h @@ -0,0 +1,42 @@ +#ifndef OGPC_PMANAGER_H_ +#define OGPC_PMANAGER_H_ + +inline namespace { + +template +struct FuncArgTraits; + +template +struct FuncArgTraits { + using type = T; +}; + +} + +#include +#include +#include + +class ProcedureManager { +public: + ProcedureManager(); + + template + void registerProcedure(const std::string &name, T&& func) { + procedures[name] = [&func](BinraryBuffer &b) -> BinaryBuffer { + using Arg = typename FuncArgTraits::type; + Arg x; + b >> x; + auto y = func(std::move(x)); + BinaryBuffer res; + res << y; + return res; + }; + } + +private: + std::map> procedures; +}; + +#endif + diff --git a/processor/pc/astuple.h b/processor/pc/astuple.h new file mode 100644 index 0000000..9b66fbf --- /dev/null +++ b/processor/pc/astuple.h @@ -0,0 +1,53 @@ +#ifndef OGPC_ASTUPLE_H_ +#define OGPC_ASTUPLE_H_ + +#include "utility/macros.h" + +#define OGPC_T__AS_DECLARETYPE_1(x) decltype(x)& +#define OGPC_T__AS_DECLARETYPE_2(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_1(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_3(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_2(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_4(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_3(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_5(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_4(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_6(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_5(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_7(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_6(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_8(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_7(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_9(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_8(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_10(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_9(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_11(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_10(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_12(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_11(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_13(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_12(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_14(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_13(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_15(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_14(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE_16(x, ...) decltype(x)&, OGPC_T__AS_DECLARETYPE_15(__VA_ARGS__) +#define OGPC_T__AS_DECLARETYPE(...) OG_M_CONCAT(OGPC_T__AS_DECLARETYPE_, OG_M_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__) + +#define OGPC_T__AS_CONST_DECLARETYPE_1(x) const decltype(x)& +#define OGPC_T__AS_CONST_DECLARETYPE_2(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_1(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_3(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_2(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_4(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_3(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_5(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_4(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_6(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_5(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_7(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_6(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_8(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_7(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_9(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_8(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_10(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_9(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_11(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_10(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_12(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_11(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_13(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_12(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_14(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_13(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_15(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_14(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE_16(x, ...) const decltype(x)&, OGPC_T__AS_CONST_DECLARETYPE_15(__VA_ARGS__) +#define OGPC_T__AS_CONST_DECLARETYPE(...) OG_M_CONCAT(OGPC_T__AS_CONST_DECLARETYPE_, OG_M_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__) + +#include + +#define OGPC_DECLARE_ASTUPLE(...) \ +std::tuple asTuple() { \ + return {__VA_ARGS__}; \ +}\ +std::tuple asTuple() const { \ + return {__VA_ARGS__}; \ +}\ + +#endif + diff --git a/processor/pc/pctypes.h b/processor/pc/pctypes.h new file mode 100644 index 0000000..df2bdac --- /dev/null +++ b/processor/pc/pctypes.h @@ -0,0 +1,50 @@ +#ifndef OGPC_PCTYPES_H_ +#define OGPC_PCTYPES_H_ + +#include +#include +#include +#include "astuple.h" + +using Player = std::uint8_t; +using Team = std::uint8_t; + +struct uuid { + std::uint64_t low, high; + + OGPC_DECLARE_ASTUPLE(low, high) +}; + +struct ImportTerrainConfig { + std::uint8_t w, h; + std::string value; + + OGPC_DECLARE_ASTUPLE(w, h, value) +}; + +struct RandomTerrainConfig { + std::uint8_t w, h; + std::uint8_t city_dense, mountain_dense, swamp_dense, light_dense; + + OGPC_DECLARE_ASTUPLE(w, h, city_dense, mountain_dense, swamp_dense, light_dense) +}; + +struct PlayerMove { + Player player; + std::uint8_t x, y, dir; + bool half; + + OGPC_DECLARE_ASTUPLE(player, x, y, dir, half) +}; + +struct PlayerRanking { + Player player; + bool is_defeated; + std::uint16_t land; + std::int32_t unit; + + OGPC_DECLARE_ASTUPLE(player, is_defeated, land, unit) +}; + +#endif + diff --git a/processor/test/cases/bbuffer.cpp b/processor/test/cases/bbuffer.cpp new file mode 100644 index 0000000..b124b42 --- /dev/null +++ b/processor/test/cases/bbuffer.cpp @@ -0,0 +1,32 @@ +#include "pc/BinaryBuffer.h" +#include "pc/astuple.h" +#include "test/u.hpp" + +inline namespace { + +struct Point { + int x, y, z; + OGPC_DECLARE_ASTUPLE(x, y, z); +}; + +AddTestCase _(10, "BinaryBuffer", [](TestCase& t) { + t.expectEq([] { + BinaryBuffer bb; + int x, y = 10; + bb << y; + bb >> x; + return x; + }, 10); + + t.expectEq>([] { + BinaryBuffer bb; + Point x{1, 2, 4}; + std::tuple y; + bb << x; + bb >> y; + return y; + }, {1, 2, 4}); +}); + +} + diff --git a/processor/test/cases/provider.cpp b/processor/test/cases/provider.cpp new file mode 100644 index 0000000..a916088 --- /dev/null +++ b/processor/test/cases/provider.cpp @@ -0,0 +1,19 @@ +#include "test/u.hpp" +#include +#include + +namespace { + +AddTestCase _(0, "tester", [](TestCase& t) { + t.expectTrue([] { return true; }); + t.expectFalse([] { return false; }); + t.expectTrue([] { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + return true; + }); + + t.expectEq([] { return 42; }, 42); + t.expectEq([] { return 10 + 1e-7; }, 10); + t.expectEq([] { return 10 + 1e-10; }, 10); +}); +} diff --git a/processor/test/main.cpp b/processor/test/main.cpp new file mode 100644 index 0000000..f9e3dd9 --- /dev/null +++ b/processor/test/main.cpp @@ -0,0 +1,5 @@ +#include "test/u.hpp" + +int main() { + return TestSuit::getInstance()->evalTestCases(); +} \ No newline at end of file diff --git a/processor/test/u.hpp b/processor/test/u.hpp new file mode 100644 index 0000000..28b483e --- /dev/null +++ b/processor/test/u.hpp @@ -0,0 +1,205 @@ +#ifndef HEADER_TEST_U_HPP +#define HEADER_TEST_U_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +struct TestDefaultEps {}; + +template<> +struct TestDefaultEps { + static constexpr float value = 1e-6; +}; + +template<> +struct TestDefaultEps { + static constexpr double value = 1e-9; +}; + +template<> +struct TestDefaultEps { + static constexpr long double value = 1e-12; +}; + +class TestCase { +public: + TestCase(int id, const char* title): ok(0), fail(0), n(0), ended(false), time_used(0) { + std::printf("[%02d]Testing %s:\n", id, title); + } + + void expectTrue(std::function func) noexcept { + expectEq(func, true); + } + + void expectFalse(std::function func) noexcept { + expectEq(func, false); + } + + template> + typename std::enable_if::value>::type expectEq( + std::function func, + T&& answer) noexcept { + auto res = runTask(func); + if (!res.has_value()) { + return onError(); + } + + if (std::abs(res.value() - answer) < EPS::value) { + onPass(); + } else { + onFail(); + } + } + + template + typename std::enable_if::value>::type expectEq( + std::function func, + T&& answer) noexcept { + auto res = runTask(func); + if (!res.has_value()) { + return onError(); + } + + if (res.value() == answer) { + onPass(); + } else { + onFail(); + } + } + + void end() noexcept { + if (ended) { + return; + } + ended = true; + + std::putchar('\n'); + for (int i = 0; i < 20; ++i) { + std::putchar('-'); + } + std::putchar('\n'); + if (fail) { + if (fail == 1) { + std::puts("1 test failed."); + } else { + std::printf("%d tests failed.\n", fail); + } + } else { + if (ok == 1) { + std::printf("OK. 1 test passed"); + } else { + std::printf("OK. %d tests passed", ok); + } + std::printf(" in %lldms.\n", static_cast(time_used.count())); + } + } + + bool hasFail() const noexcept { + return fail > 0; + } + +private: + void putchar(char c) noexcept { + n += 1; + if (n > 1 && n % 15 == 1) { + std::putchar('\n'); + } + std::putchar(c); + } + + void onPass() noexcept { + ok += 1; + putchar('.'); + } + + void onFail() noexcept { + fail += 1; + putchar('F'); + } + + void onError() noexcept { + fail += 1; + putchar('E'); + } + + template + std::optional runTask(std::function func) noexcept { + auto start = std::chrono::high_resolution_clock::now(); + try { + T res = func(); + auto end = std::chrono::high_resolution_clock::now(); + auto dt = end - start; + time_used += std::chrono::duration_cast(dt); + return {res}; + } catch (...) { + return std::nullopt; + } + } + + int ok, fail, n; + bool ended; + std::chrono::milliseconds time_used; +}; + +class TestSuit { + TestSuit(const TestSuit&) = delete; + TestSuit(TestSuit&&) = delete; + +public: + static TestSuit* getInstance() { + if (!instance) { + instance = new TestSuit(); + } + return instance; + } + + void addTestCase(int id, const char* title, std::function f) { + tc.emplace_back(id, title, f); + } + + int evalTestCases() { + std::sort(tc.begin(), tc.end(), [](const auto &x, const auto &y) { + return std::get<0>(x) < std::get<0>(y); + }); + + for (auto [id, title, func] : tc) { + TestCase t(id, title); + func(t); + t.end(); + std::putchar('\n'); + if (t.hasFail()) { + return 1; + } + } + tc.clear(); + return 0; + } + +private: + static inline TestSuit* instance = nullptr; + TestSuit() {} + + std::vector>> tc; +}; + +class AddTestCase { + AddTestCase() = delete; + AddTestCase(const AddTestCase&) = delete; + AddTestCase(AddTestCase&&) = delete; + +public: + AddTestCase(int id, const char* tilte, std::function func) { + TestSuit::getInstance()->addTestCase(id, tilte, func); + } +}; + +#endif // HEADER_TEST_U_HPP + diff --git a/processor/utility/macros.h b/processor/utility/macros.h new file mode 100644 index 0000000..6f91797 --- /dev/null +++ b/processor/utility/macros.h @@ -0,0 +1,27 @@ +#ifndef OG_MACROS_H_ +#define OG_MACROS_H_ + +#define OG_M_ARG_COUNT__( \ +_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ +_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ +_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ +_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ +_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ +_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ +_61, _62, _63, N, ...) \ +N + +#define OG_M_ARG_COUNT(...) OG_M_ARG_COUNT__(__VA_ARGS__ \ +, 63, 62, 61, 60, \ +59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ +49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ +39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ +29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ +19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define OG_M_CONCAT__(a, b) a##b +#define OG_M_CONCAT(a, b) OG_M_CONCAT__(a, b) + +#endif + diff --git a/processor/xmake.lua b/processor/xmake.lua new file mode 100644 index 0000000..855169e --- /dev/null +++ b/processor/xmake.lua @@ -0,0 +1,28 @@ +set_project("opengenerals-processor") +set_basename("ogprocessor") +set_version("0.1.0") +set_languages("c++20") +set_targetdir("build") + +add_includedirs(".") + +add_files("pc/*.cpp", "utility/*.cpp") +target("main") + set_default(true) + set_kind("binary") + add_files("main.cpp") + set_warnings("allextra") + +target("test") + set_default(false) + set_kind("binary") + set_prefixname("test-") + + add_files("test/**/*.cpp") + add_files("test/main.cpp") + + set_warnings("allextra") + set_optimize("none") + set_symbols("debug") + add_defines("DEBUG") +