Skip to content

Instantly share code, notes, and snippets.

@fredemmott
Created September 25, 2024 12:25
Show Gist options
  • Save fredemmott/cff324eaf580efddb1ca1b530646ca51 to your computer and use it in GitHub Desktop.
Save fredemmott/cff324eaf580efddb1ca1b530646ca51 to your computer and use it in GitHub Desktop.
// Copyright 2024 Fred Emmott <[email protected]>
// SPDX-License-Identifier: ISC
#include <algorithm>
#include <concepts>
#include <functional>
#include <ranges>
#include <string>
#include <print>
#include <nlohmann/json.hpp>
template <size_t N>
struct StringTemplateParameter {
StringTemplateParameter() = delete;
consteval StringTemplateParameter(char const (&init)[N]) {
std::ranges::copy(init, value);
}
char value[N] {};
constexpr operator std::string_view() const noexcept {
return std::string_view { value };
}
};
template <>
struct StringTemplateParameter<0> {};
template <StringTemplateParameter T>
consteval auto operator""_tp() {
return T;
}
template<class TObj, auto TPropertyName, auto TGetter, auto TSetter>
struct JSProp {
using value_type = std::decay_t<std::invoke_result_t<
decltype(TGetter),
TObj*>>;
static constexpr std::string_view name_v { TPropertyName };
[[nodiscard]] constexpr static value_type Get(const TObj& obj) noexcept {
return std::invoke(TGetter, obj);
}
template<std::convertible_to<value_type> TNewValue>
static void Set(TObj& obj, TNewValue&& value) noexcept {
std::invoke(TSetter, obj, std::forward<TNewValue>(value));
}
};
struct JSProp_Compile_Error { JSProp_Compile_Error() = delete; };
template<class T, std::size_t N, std::size_t Count>
struct JSProp_Compile_Error_Invalid_Index : JSProp_Compile_Error {};
#define JSPROP_IMPL_ID(x) (x)
#define JSPROP_IMPL_COUNTER JSPROP_IMPL_ID(__COUNTER__)
#define BEGIN_EXPORT_JSOBJECT(name) \
static constexpr auto JSObject_class_name { #name }; \
using jsobject_type = name; \
static constexpr auto jsobject_impl_prop_counter_init = JSPROP_IMPL_COUNTER; \
template<std::size_t N> struct JSProp_Impl {};
#define END_EXPORT_JSOBJECT() \
private: \
static constexpr auto jsobject_impl_prop_counter_fini = JSPROP_IMPL_COUNTER; \
public: \
static constexpr auto jsprop_count_v = jsobject_impl_prop_counter_fini - (jsobject_impl_prop_counter_init + 1); \
template<std::size_t N> \
using JSProp = std::conditional_t<N < jsprop_count_v, JSProp_Impl<N>, JSProp_Compile_Error_Invalid_Index<jsobject_type, N, jsprop_count_v>>; \
friend void to_json(nlohmann::json& j, const jsobject_type& v) { \
JSObject_to_json(j, v); \
}
#define EXPORT_JSPROP(name) \
template<> struct JSProp_Impl<JSPROP_IMPL_COUNTER - (jsobject_impl_prop_counter_init + 1)> : ::JSProp< \
jsobject_type, \
StringTemplateParameter { #name }, \
&jsobject_type::Get##name, &jsobject_type::Set##name> {};
template<class T, std::size_t PropertyIndex = 0>
void JSObject_to_json(nlohmann::json& j, const T& o) {
if constexpr(PropertyIndex == T::jsprop_count_v) {
return;
} else {
using Prop = T::template JSProp<PropertyIndex>;
j.emplace(Prop::name_v, Prop::Get(o));
JSObject_to_json<T, PropertyIndex + 1>(j, o);
}
}
struct MyStruct {
BEGIN_EXPORT_JSOBJECT(MyStruct);
public:
std::string mFoo;
int mBar {};
std::string GetFoo() const noexcept { return mFoo; }
void SetFoo(const std::string_view v) noexcept { mFoo = std::string { v }; }
EXPORT_JSPROP(Foo);
int GetBar() const noexcept { return mBar; }
void SetBar(const int v) noexcept { mBar = v; };
EXPORT_JSPROP(Bar);
END_EXPORT_JSOBJECT();
};
int main(int argc, char** argv) {
nlohmann::json j = MyStruct {
.mFoo = "hello",
.mBar = 456,
};
std::println("{}", j.dump(2));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment