// // immer: immutable data structures for C++ // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #pragma once #include <scm/detail/finalizer_wrapper.hpp> #include <scm/detail/define.hpp> #include <string> namespace scm { namespace detail { template <typename T> struct foreign_type_storage { static SCM data; }; template <typename T> SCM foreign_type_storage<T>::data = SCM_UNSPECIFIED; template <typename T> struct convert_foreign_type { using storage_t = foreign_type_storage<T>; static T& to_cpp(SCM v) { assert(storage_t::data != SCM_UNSPECIFIED && "can not convert to undefined type"); scm_assert_foreign_object_type(storage_t::data, v); return *(T*)scm_foreign_object_ref(v, 0); } template <typename U> static SCM to_scm(U&& v) { assert(storage_t::data != SCM_UNSPECIFIED && "can not convert from undefined type"); return scm_make_foreign_object_1( storage_t::data, new (scm_gc_malloc(sizeof(T), "scmpp")) T( std::forward<U>(v))); } }; // Assume that every other type is foreign template <typename T> struct convert<T, std::enable_if_t<!std::is_fundamental<T>::value && // only value types are supported at // the moment but the story might // change later... !std::is_pointer<T>::value>> : convert_foreign_type<T> { }; template <typename Tag, typename T, int Seq=0> struct type_definer : move_sequence { using this_t = type_definer; using next_t = type_definer<Tag, T, Seq + 1>; std::string type_name_ = {}; scm_t_struct_finalize finalizer_ = nullptr; type_definer(type_definer&&) = default; type_definer(std::string type_name) : type_name_(std::move(type_name)) {} ~type_definer() { if (!moved_from_) { using storage_t = detail::foreign_type_storage<T>; assert(storage_t::data == SCM_UNSPECIFIED); storage_t::data = scm_make_foreign_object_type( scm_from_utf8_symbol(("<" + type_name_ + ">").c_str()), scm_list_1(scm_from_utf8_symbol("data")), finalizer_); } } template <int Seq2, typename Enable=std::enable_if_t<Seq2 + 1 == Seq>> type_definer(type_definer<Tag, T, Seq2> r) : move_sequence{std::move(r)} , type_name_{std::move(r.type_name_)} , finalizer_{std::move(r.finalizer_)} {} next_t constructor() && { define_impl<this_t>(type_name_, [] { return T{}; }); return { std::move(*this) }; } template <typename Fn> next_t constructor(Fn fn) && { define_impl<this_t>(type_name_, fn); return { std::move(*this) }; } next_t finalizer() && { finalizer_ = (scm_t_struct_finalize) +finalizer_wrapper<Tag>( [] (T& x) { x.~T(); }); return { std::move(*this) }; } template <typename Fn> next_t finalizer(Fn fn) && { finalizer_ = (scm_t_struct_finalize) +finalizer_wrapper<Tag>(fn); return { std::move(*this) }; } next_t maker() && { define_impl<this_t>("make-" + type_name_, [] { return T{}; }); return { std::move(*this) }; } template <typename Fn> next_t maker(Fn fn) && { define_impl<this_t>("make-" + type_name_, fn); return { std::move(*this) }; } template <typename Fn> next_t define(std::string name, Fn fn) && { define_impl<this_t>(type_name_ + "-" + name, fn); return { std::move(*this) }; } }; } // namespace detail template <typename Tag, typename T=Tag> detail::type_definer<Tag, T> type(std::string type_name) { return { type_name }; } } // namespace scm