Add procedure call impl

Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
方而静 2024-02-04 13:47:53 +08:00
commit f0445542ce
Signed by: szTom
GPG Key ID: 072D999D60C6473C
12 changed files with 646 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
build
*.zip
# Executables
*.pch
*.out
*.exe
*.dll
*.so
# Node.JS
package-lock.json
node_modules
# Editor
*.swp
.vscode
# Xmake
.xmake

View File

@ -0,0 +1,36 @@
#include "BinaryBuffer.h"
#include <cstdint>
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;
}

128
processor/pc/BinaryBuffer.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef OG_BINARY_BUFFER_H_
#define OG_BINARY_BUFFER_H_
#include <cstdint>
#include <tuple>
#include <vector>
#include <string>
#include <deque>
#include <concepts>
#include <string_view>
#include <iostream>
using byte = unsigned char;
inline namespace {
template <typename T>
concept IsTupleLike = requires(T t) {
std::tuple_size<T>::value;
};
template <typename T>
concept IsBasicType = std::integral<T> || std::is_enum<T>::value
|| std::same_as<T, float> || std::same_as<T, double>;
template <typename T>
concept IsTupleConvertable = requires(T t) {
IsTupleLike<decltype(t.asTuple())>;
};
static_assert(IsTupleLike<std::tuple<int, int, int>>);
}
class BinaryBuffer {
public:
BinaryBuffer();
template <typename T>
requires IsBasicType<T>
BinaryBuffer& operator>>(T& x) {
if (sizeof(T) <= data.size()) {
auto ptr = reinterpret_cast<byte*>(&x);
for (std::size_t i = 0; i < sizeof(T); ++i) {
ptr[i] = data.front();
data.pop_front();
}
}
return *this;
}
template <typename T>
requires IsBasicType<T>
BinaryBuffer& operator<<(const T& x) {
auto ptr = reinterpret_cast<const byte*>(&x);
for (std::size_t i = 0; i < sizeof(T); ++i) {
data.push_back(ptr[i]);
}
return *this;
}
template <typename T>
BinaryBuffer& operator<<(const std::vector<T>& x) {
uint32_t len = x.size();
*this << len;
for (const T& element : x)
*this << element;
return *this;
}
template <typename T>
BinaryBuffer& operator>>(std::vector<T>& x) {
uint32_t len;
*this >> len;
x.resize(len);
for (std::uint32_t i = 0; i < len; ++i)
*this >> x[i];
return *this;
}
template <typename T>
requires IsTupleLike<T>
BinaryBuffer& operator<<(const T& x) {
std::apply([this](const auto&... elements) {
(*this << ... << elements);
}, x);
return *this;
}
template <typename T>
requires IsTupleLike<T>
BinaryBuffer& operator>>(T& x) {
std::apply([this](auto&... elements) {
(*this >> ... >> elements);
}, x);
return *this;
}
template <typename T>
requires IsTupleConvertable<T>
BinaryBuffer& operator<<(const T &x) {
*this << x.asTuple();
return *this;
}
template <typename T>
requires IsTupleConvertable<T>
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<byte> data;
};
std::ostream& operator<<(std::ostream &, const BinaryBuffer &);
#endif

View File

@ -0,0 +1,42 @@
#ifndef OGPC_PMANAGER_H_
#define OGPC_PMANAGER_H_
inline namespace {
template <typename T>
struct FuncArgTraits;
template <typename R, typename T>
struct FuncArgTraits<R(T)> {
using type = T;
};
}
#include <map>
#include <string>
#include <functional>
class ProcedureManager {
public:
ProcedureManager();
template <typename T>
void registerProcedure(const std::string &name, T&& func) {
procedures[name] = [&func](BinraryBuffer &b) -> BinaryBuffer {
using Arg = typename FuncArgTraits<T::operator()>::type;
Arg x;
b >> x;
auto y = func(std::move(x));
BinaryBuffer res;
res << y;
return res;
};
}
private:
std::map<std::string, std::function<BinaryBuffer(BinaryBuffer&)>> procedures;
};
#endif

53
processor/pc/astuple.h Normal file
View File

@ -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 <tuple>
#define OGPC_DECLARE_ASTUPLE(...) \
std::tuple<OGPC_T__AS_DECLARETYPE(__VA_ARGS__)> asTuple() { \
return {__VA_ARGS__}; \
}\
std::tuple<OGPC_T__AS_CONST_DECLARETYPE(__VA_ARGS__)> asTuple() const { \
return {__VA_ARGS__}; \
}\
#endif

50
processor/pc/pctypes.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef OGPC_PCTYPES_H_
#define OGPC_PCTYPES_H_
#include <cctypes>
#include <string>
#include <vector>
#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

View File

@ -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<int>([] {
BinaryBuffer bb;
int x, y = 10;
bb << y;
bb >> x;
return x;
}, 10);
t.expectEq<std::tuple<int, int, int>>([] {
BinaryBuffer bb;
Point x{1, 2, 4};
std::tuple<int, int, int> y;
bb << x;
bb >> y;
return y;
}, {1, 2, 4});
});
}

View File

@ -0,0 +1,19 @@
#include "test/u.hpp"
#include <chrono>
#include <thread>
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<int>([] { return 42; }, 42);
t.expectEq<float>([] { return 10 + 1e-7; }, 10);
t.expectEq<double>([] { return 10 + 1e-10; }, 10);
});
}

5
processor/test/main.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "test/u.hpp"
int main() {
return TestSuit::getInstance()->evalTestCases();
}

205
processor/test/u.hpp Normal file
View File

@ -0,0 +1,205 @@
#ifndef HEADER_TEST_U_HPP
#define HEADER_TEST_U_HPP
#include <chrono>
#include <cstdio>
#include <exception>
#include <functional>
#include <optional>
#include <type_traits>
#include <tuple>
#include <vector>
#include <algorithm>
template<typename T>
struct TestDefaultEps {};
template<>
struct TestDefaultEps<float> {
static constexpr float value = 1e-6;
};
template<>
struct TestDefaultEps<double> {
static constexpr double value = 1e-9;
};
template<>
struct TestDefaultEps<long double> {
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<bool()> func) noexcept {
expectEq<bool>(func, true);
}
void expectFalse(std::function<bool()> func) noexcept {
expectEq<bool>(func, false);
}
template<typename T, typename EPS = TestDefaultEps<T>>
typename std::enable_if<std::is_floating_point<T>::value>::type expectEq(
std::function<T()> 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 T>
typename std::enable_if<!std::is_floating_point<T>::value>::type expectEq(
std::function<T()> 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<long long>(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<typename T>
std::optional<T> runTask(std::function<T()> 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<std::chrono::milliseconds>(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<void(TestCase&)> 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<std::tuple<int, const char*, std::function<void(TestCase&)>>> tc;
};
class AddTestCase {
AddTestCase() = delete;
AddTestCase(const AddTestCase&) = delete;
AddTestCase(AddTestCase&&) = delete;
public:
AddTestCase(int id, const char* tilte, std::function<void(TestCase&)> func) {
TestSuit::getInstance()->addTestCase(id, tilte, func);
}
};
#endif // HEADER_TEST_U_HPP

View File

@ -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

28
processor/xmake.lua Normal file
View File

@ -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")