|
|
@@ -0,0 +1,141 @@
|
|
|
+//
|
|
|
+// typecast.h
|
|
|
+// reflection
|
|
|
+//
|
|
|
+// Created by Sam Jaffe on 7/4/22.
|
|
|
+// Copyright © 2022 Sam Jaffe. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+#pragma once
|
|
|
+
|
|
|
+#include <functional>
|
|
|
+#include <typeindex>
|
|
|
+#include <unordered_map>
|
|
|
+#include <utility>
|
|
|
+
|
|
|
+#include "reflection/forward.h"
|
|
|
+#include "reflection/object.h"
|
|
|
+
|
|
|
+namespace reflection {
|
|
|
+template <typename T> class TypeCast {
|
|
|
+public:
|
|
|
+ static bool has_cast(Object const & obj) { return get_.count(obj.type()); }
|
|
|
+
|
|
|
+ static T get(Object const & obj) { return get_.at(obj.type())(obj); }
|
|
|
+
|
|
|
+ static void set(Object & obj, T const & value) {
|
|
|
+ set_.at(obj.type())(obj, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ operator bool() const { return true; }
|
|
|
+
|
|
|
+ template <typename I, typename O, typename S>
|
|
|
+ TypeCast & bind(T (*to)(I), O (*from)(S)) {
|
|
|
+ return bind(std::function(to), std::function(from));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename I, typename O, typename S>
|
|
|
+ TypeCast & bind(std::function<T(I)> to, std::function<O(S)> from) {
|
|
|
+ bind_impl(to, from);
|
|
|
+ TypeCast<std::decay_t<I>>().template bind_impl(from, to);
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename V> TypeCast & bind() {
|
|
|
+ if constexpr (!std::is_same_v<V, T>) {
|
|
|
+ bind_impl<V>();
|
|
|
+ TypeCast<V>().template bind_impl<T>();
|
|
|
+ }
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename V1, typename V2, typename... Vs> TypeCast & bind() {
|
|
|
+ return bind<V1>().template bind<V2, Vs...>();
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ template <typename I, typename O, typename S>
|
|
|
+ void bind_impl(std::function<T(I)> to, std::function<O(S)> from) {
|
|
|
+ static_assert(std::is_same_v<std::decay_t<S>, T>,
|
|
|
+ "this types must be compatible");
|
|
|
+ static_assert(std::is_same_v<std::decay_t<I>, std::decay_t<I>>,
|
|
|
+ "cast types must be compatible");
|
|
|
+ using V = std::decay_t<I>;
|
|
|
+ get_.emplace(typeid(V),
|
|
|
+ [to](auto & obj) { return to(static_cast<V const &>(obj)); });
|
|
|
+ set_.emplace(typeid(V), [from](auto & obj, T const & val) {
|
|
|
+ static_cast<V &>(obj) = from(val);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename V> void bind_impl() {
|
|
|
+ get_.emplace(typeid(V), [](auto & obj) {
|
|
|
+ return static_cast<T>(static_cast<V const &>(obj));
|
|
|
+ });
|
|
|
+ set_.emplace(typeid(V), [](auto & obj, T val) {
|
|
|
+ static_cast<V &>(obj) = static_cast<V>(val);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ template <typename S> friend class TypeCast;
|
|
|
+ static TypeMap<std::function<T(Object const &)>> get_;
|
|
|
+ static TypeMap<std::function<void(Object &, T const &)>> set_;
|
|
|
+};
|
|
|
+
|
|
|
+template <typename T>
|
|
|
+TypeMap<std::function<T(Object const &)>> TypeCast<T>::get_;
|
|
|
+template <typename T>
|
|
|
+TypeMap<std::function<void(Object &, T const &)>> TypeCast<T>::set_;
|
|
|
+}
|
|
|
+
|
|
|
+#if defined REFLECTION_TYPE_CAST_IMPLEMENTATION
|
|
|
+namespace reflection {
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(bool);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(int16_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(int32_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(int64_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(uint16_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(uint32_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(uint64_t);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(float);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(double);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(long double);
|
|
|
+INSTANTIATE_REFLECTION_TYPECAST(std::string);
|
|
|
+
|
|
|
+// Bind boolean types to all integer types
|
|
|
+REFLECTION_TYPE_CAST(bool)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+// Bind all integer types (wider than 1byte) to each other.
|
|
|
+REFLECTION_TYPE_CAST(int32_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+REFLECTION_TYPE_CAST(uint32_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+REFLECTION_TYPE_CAST(int16_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+REFLECTION_TYPE_CAST(uint16_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+REFLECTION_TYPE_CAST(int64_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+REFLECTION_TYPE_CAST(uint64_t)
|
|
|
+ .bind<int16_t, int32_t, int64_t>()
|
|
|
+ .bind<uint16_t, uint32_t, uint64_t>();
|
|
|
+
|
|
|
+// Mututally bind all floating point numbers
|
|
|
+REFLECTION_TYPE_CAST(float).bind<float, double, long double>();
|
|
|
+REFLECTION_TYPE_CAST(double).bind<float, double, long double>();
|
|
|
+REFLECTION_TYPE_CAST(long double).bind<float, double, long double>();
|
|
|
+}
|
|
|
+#endif
|