Created
September 25, 2024 12:25
-
-
Save fredemmott/cff324eaf580efddb1ca1b530646ca51 to your computer and use it in GitHub Desktop.
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
// 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