Skip to content

Instantly share code, notes, and snippets.

@Klowner
Created March 13, 2018 15:47
Show Gist options
  • Save Klowner/68eda17e9f5e649ba76da18d87dcfd82 to your computer and use it in GitHub Desktop.
Save Klowner/68eda17e9f5e649ba76da18d87dcfd82 to your computer and use it in GitHub Desktop.
// (C) 2018 Mark Riedesel <mark@klowner.com>
// http://sensible.website/posts/serializing-tasks-with-c++-metaprogramming/
// The following code is licensed under the MIT license.
#include <cstdint>
#include <cstring>
#include <iostream>
using CommandId = uint16_t;
struct Stream {
Stream()
: write_pos(0)
, read_pos(0)
{
memset(&buffer, 0, sizeof(buffer));
}
int write(void *_src, size_t _bytes) {
if (write_pos + _bytes < capacity) {
memcpy(&buffer[write_pos], _src, _bytes);
write_pos += _bytes;
return _bytes; // return number of byte written
}
return 0; // failure
}
int read(void *_dst, size_t _bytes) {
if (read_pos + _bytes < capacity) {
memcpy(_dst, &buffer[read_pos], _bytes);
read_pos += _bytes;
return _bytes; // return number of bytes read
}
return 0; // failure
}
CommandId getNextCommandId() const {
CommandId id;
memcpy(&id, &buffer[read_pos], sizeof(id));
return id;
}
static constexpr const size_t capacity { 1024 };
uint8_t buffer[capacity];
size_t write_pos;
size_t read_pos;
};
struct CommandType {
enum Enum {
End = 0,
CallTask,
};
};
using TaskFunctionPtr = void(*)(Stream&);
struct CallTaskCommand {
CommandId type;
TaskFunctionPtr function_ptr;
};
// Tasks
struct task_a {
int execute(int foo, int bar) {
std::cout << "executing task_a with args: " << foo << ' ' << bar << '\n';
return foo * bar;
}
};
struct task_b {
int execute(int foo, int bar, int baz) {
std::cout << "executing task_b with args: " << foo << ' ' << bar << ' ' << baz << '\n';
return foo * bar * baz;
}
};
template <typename A>
inline A task_decode_arg(Stream &_stream) {
A arg;
_stream.read(&arg, sizeof(A));
std::cout << "decoding: " << arg << '\n';
return arg;
}
template <typename T, typename ...Args>
void task_decode(Stream &_stream) {
T instance;
instance.execute(task_decode_arg<Args>(_stream) ...);
}
template <typename T, typename Head>
inline void constexpr task_encode_r(Stream &stream, Head &&_head) {
std::cout << "encoding: " << _head << '\n';
stream.write(&_head, sizeof(Head));
}
template <typename T, typename Head, typename ...Tail>
inline void constexpr task_encode_r(Stream &stream, Head &&_head, Tail &&..._tail) {
task_encode_r<T>(stream, _tail...);
task_encode_r<T>(stream, _head);
}
template <typename T, typename ...Args>
void constexpr task_encode(Stream &_stream, Args &&..._args) {
CallTaskCommand call_task;
call_task.type = CommandType::CallTask;
call_task.function_ptr = &task_decode<T, Args...>;
_stream.write(&call_task, sizeof(CallTaskCommand));
task_encode_r<T>(_stream, _args...);
}
void execute(Stream &_stream) {
CommandId command_type;
while ((command_type = _stream.getNextCommandId()) != CommandType::End) {
switch(command_type) {
case CommandType::CallTask: {
CallTaskCommand cmd;
_stream.read(&cmd, sizeof(CallTaskCommand));
cmd.function_ptr(_stream);
} break;
case CommandType::End:
default:
return;
}
}
}
void test() {
Stream stream;
std::cout << "recording phase..." << '\n';
task_encode<task_a>(stream, 1, 2);
task_encode<task_b>(stream, 4, 5, 6);
std::cout << "\nexecution phase..." << '\n';
for (int i = 0; i < 4; i++) {
std::cout << "execute iteration " << i << '\n';
stream.read_pos = 0;
execute(stream);
}
}
int main() {
test();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment