feat use inja generate
This commit is contained in:
parent
ab434df5fc
commit
fd8ae6c6bd
4753
3rdparty/inja/inja.hpp
vendored
Normal file
4753
3rdparty/inja/inja.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24765
3rdparty/inja/nlohmann/json.hpp
vendored
Normal file
24765
3rdparty/inja/nlohmann/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1195
3rdparty/mustache/mustache.hpp
vendored
1195
3rdparty/mustache/mustache.hpp
vendored
File diff suppressed because it is too large
Load Diff
2
3rdparty/sled
vendored
2
3rdparty/sled
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f31657640f5731c2f86206190fe1d59728225795
|
Subproject commit fcce11249be00ca0c7f8c2e75938de4b3e5f26da
|
@ -41,5 +41,6 @@ endif()
|
|||||||
target_include_directories(meta PRIVATE
|
target_include_directories(meta PRIVATE
|
||||||
${CLANG_INCLUDE_DIR}
|
${CLANG_INCLUDE_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/mustache)
|
${CMAKE_CURRENT_SOURCE_DIR}/runtime
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/inja)
|
||||||
target_link_libraries(meta PRIVATE sled ${CLANG_LIB_LIBRARIES})
|
target_link_libraries(meta PRIVATE sled ${CLANG_LIB_LIBRARIES})
|
||||||
|
329
runtime/any.h
Normal file
329
runtime/any.h
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/**
|
||||||
|
* Port of boost::any for C++11 compilers.
|
||||||
|
* See http://www.boost.org/libs/any for Documentation.
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* + http://en.cppreference.com/w/cpp/any
|
||||||
|
* + http://en.cppreference.com/w/cpp/experimental/any
|
||||||
|
* + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
|
||||||
|
* + https://cplusplus.github.io/LWG/lwg-active.html#2509
|
||||||
|
*
|
||||||
|
* Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
|
||||||
|
* Copyright Claudio Fantacci, 2018. All rights reserved.
|
||||||
|
*
|
||||||
|
* what: variant type boost::any
|
||||||
|
* who: contributed by Kevlin Henney,
|
||||||
|
* with features contributed and bugs found by Antony Polukhin, Ed Brey, Mark Rodgers, Peter Dimov and James Curran,
|
||||||
|
* with C++11 compiler port by Claudio Fantacci
|
||||||
|
* when: July 2001, April 2013 - May 2013, September 2018
|
||||||
|
*
|
||||||
|
* Distributed under the Boost Software License, Version 1.0.
|
||||||
|
* (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef META_ANY_H
|
||||||
|
#define META_ANY_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
namespace meta {
|
||||||
|
namespace reflection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class any describes a type-safe container for single values of any type.
|
||||||
|
* An object of class any stores an instance of any type that satisfies the
|
||||||
|
* constructor requirements or is empty, and this is referred to as the state
|
||||||
|
* of the class any object. The stored instance is called the contained object.
|
||||||
|
* Two states are equivalent if they are either both empty or if both are not
|
||||||
|
* empty and if the contained objects are equivalent.
|
||||||
|
* The non-member any_cast functions provide type-safe access to the contained object.
|
||||||
|
*/
|
||||||
|
class any final {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs an empty object.
|
||||||
|
*/
|
||||||
|
any() noexcept : content(0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies content of other into a new instance, so that any content is equivalent
|
||||||
|
* in both type and value to those of other prior to the constructor call,
|
||||||
|
* or empty if other is empty.
|
||||||
|
*/
|
||||||
|
any(const any &other) : content(other.content ? other.content->clone() : 0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves content of other into a new instance, so that any content is equivalent
|
||||||
|
* in both type and value to those of other prior to the constructor call,
|
||||||
|
* or empty if other is empty.
|
||||||
|
*/
|
||||||
|
any(any &&other) noexcept : content(other.content) { other.content = 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an object with initial content an object of type std::decay_t<ValueType>,
|
||||||
|
* direct-initialized from std::forward<ValueType>(value). If
|
||||||
|
* std::is_copy_constructible<std::decay_t<ValueType>>::value is false, the program is ill-formed.
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
any(const ValueType &value)
|
||||||
|
: content(
|
||||||
|
new holder<typename std::remove_cv<typename std::decay<const ValueType>::type>::type>(
|
||||||
|
value))
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an object with initial content an object of type std::decay_t<ValueType>,
|
||||||
|
* direct-initialized from std::forward<ValueType>(value). If
|
||||||
|
* std::is_copy_constructible<std::decay_t<ValueType>>::value is false, the program is ill-formed.
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
any(ValueType &&value,
|
||||||
|
typename std::enable_if<!std::is_same<any &, ValueType>::value>::type * = 0,
|
||||||
|
typename std::enable_if<!std::is_const<ValueType>::value>::type * = 0)
|
||||||
|
: content(
|
||||||
|
new holder<typename std::decay<ValueType>::type>(static_cast<ValueType &&>(value)))
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruct the object.
|
||||||
|
*/
|
||||||
|
~any() noexcept { delete content; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns contents to the contained value.
|
||||||
|
* Assigns by copying the state of rhs, as if by any(rhs).swap(*this).
|
||||||
|
*
|
||||||
|
* @param rhs object whose contained value to assign
|
||||||
|
*/
|
||||||
|
any &operator=(const any &rhs)
|
||||||
|
{
|
||||||
|
any(rhs).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns contents to the contained value.
|
||||||
|
* Assigns by moving the state of rhs, as if by any(std::move(rhs)).swap(*this).
|
||||||
|
* rhs is left in a valid but unspecified state after the assignment.
|
||||||
|
*
|
||||||
|
* @param rhs object whose contained value to assign
|
||||||
|
*/
|
||||||
|
any &operator=(any &&rhs) noexcept
|
||||||
|
{
|
||||||
|
rhs.swap(*this);
|
||||||
|
any().swap(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns contents to the contained value.
|
||||||
|
* Assigns the type and value of rhs, as if by any(std::forward<ValueType>(rhs)).swap(*this).
|
||||||
|
* This overload only participates in overload resolution if std::decay_t<ValueType> is not
|
||||||
|
* the same type as any and std::is_copy_constructible_v<std::decay_t<ValueType>> is true.
|
||||||
|
*
|
||||||
|
* @param rhs object whose contained value to assign
|
||||||
|
*/
|
||||||
|
template<class ValueType>
|
||||||
|
any &operator=(ValueType &&rhs)
|
||||||
|
{
|
||||||
|
any(static_cast<ValueType &&>(rhs)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not empty, destroys the contained object.
|
||||||
|
*/
|
||||||
|
void reset() noexcept { any().swap(*this); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps the content of two any objects.
|
||||||
|
*
|
||||||
|
* @param other object to swap with
|
||||||
|
*/
|
||||||
|
any &swap(any &rhs) noexcept
|
||||||
|
{
|
||||||
|
std::swap(content, rhs.content);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the object contains a value.
|
||||||
|
*
|
||||||
|
* @return true if instance contains a value, otherwise false.
|
||||||
|
*/
|
||||||
|
bool has_value() const noexcept { return content; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the contained type.
|
||||||
|
*
|
||||||
|
* The typeid of the contained value if instance is non-empty, otherwise typeid(void).
|
||||||
|
*/
|
||||||
|
const std::type_info &type() const noexcept { return content ? content->type() : typeid(void); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
class placeholder {
|
||||||
|
public:
|
||||||
|
virtual ~placeholder() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual const std::type_info &type() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual placeholder *clone() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ValueType>
|
||||||
|
class holder : public placeholder {
|
||||||
|
public:
|
||||||
|
holder(const ValueType &value) : held(value) {}
|
||||||
|
|
||||||
|
holder(ValueType &&value) : held(static_cast<ValueType &&>(value)) {}
|
||||||
|
|
||||||
|
virtual const std::type_info &type() const noexcept { return typeid(ValueType); }
|
||||||
|
|
||||||
|
virtual placeholder *clone() const { return new holder(held); }
|
||||||
|
|
||||||
|
ValueType held;
|
||||||
|
|
||||||
|
private:
|
||||||
|
holder &operator=(const holder &);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename ValueType>
|
||||||
|
friend ValueType *any_cast(any *) noexcept;
|
||||||
|
|
||||||
|
placeholder *content;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overloads the std::swap algorithm for std::any. Swaps the content of two any objects by calling lhs.swap(rhs).
|
||||||
|
*
|
||||||
|
* @param lhs objects to swap
|
||||||
|
* @param rhs objects to swap
|
||||||
|
*/
|
||||||
|
inline void
|
||||||
|
swap(any &lhs, any &rhs) noexcept
|
||||||
|
{
|
||||||
|
lhs.swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a type of object to be thrown by the value-returning forms of libanyboost::any_cast on failure.
|
||||||
|
*/
|
||||||
|
class bad_any_cast : public std::bad_cast {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Returns the explanatory string.
|
||||||
|
*
|
||||||
|
* Pointer to a null-terminated string with explanatory information. The pointer is guaranteed to be
|
||||||
|
* valid at least until the exception object from which it is obtained is destroyed, or until a
|
||||||
|
* non-const member function on the exception object is called.
|
||||||
|
*/
|
||||||
|
virtual const char *what() const noexcept override { return "bad any_cast"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs type-safe access to the contained object.
|
||||||
|
*
|
||||||
|
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||||
|
* ValueType does not match that of the contents of operand.
|
||||||
|
*
|
||||||
|
* @param operand target any object
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
ValueType *
|
||||||
|
any_cast(any *operand) noexcept
|
||||||
|
{
|
||||||
|
return operand && operand->type() == typeid(ValueType)
|
||||||
|
? std::addressof(
|
||||||
|
static_cast<any::holder<typename std::remove_cv<ValueType>::type> *>(operand->content)
|
||||||
|
->held)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs type-safe access to the contained object.
|
||||||
|
*
|
||||||
|
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||||
|
* ValueType does not match that of the contents of operand.
|
||||||
|
*
|
||||||
|
* @param operand target any object
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
inline const ValueType *
|
||||||
|
any_cast(const any *operand) noexcept
|
||||||
|
{
|
||||||
|
return any_cast<ValueType>(const_cast<any *>(operand));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs type-safe access to the contained object.
|
||||||
|
*
|
||||||
|
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||||
|
* ValueType does not match that of the contents of operand.
|
||||||
|
*
|
||||||
|
* @param operand target any object
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
ValueType
|
||||||
|
any_cast(any &operand)
|
||||||
|
{
|
||||||
|
typedef typename std::remove_reference<ValueType>::type nonref;
|
||||||
|
|
||||||
|
nonref *result = any_cast<nonref>(std::addressof(operand));
|
||||||
|
if (!result) throw bad_any_cast();
|
||||||
|
|
||||||
|
typedef typename std::conditional<std::is_reference<ValueType>::value,
|
||||||
|
ValueType,
|
||||||
|
typename std::add_lvalue_reference<ValueType>::type>::type
|
||||||
|
ref_type;
|
||||||
|
|
||||||
|
return static_cast<ref_type>(*result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs type-safe access to the contained object.
|
||||||
|
*
|
||||||
|
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||||
|
* ValueType does not match that of the contents of operand.
|
||||||
|
*
|
||||||
|
* @param operand target any object
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
inline ValueType
|
||||||
|
any_cast(const any &operand)
|
||||||
|
{
|
||||||
|
typedef typename std::remove_reference<ValueType>::type nonref;
|
||||||
|
return any_cast<const nonref &>(const_cast<any &>(operand));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs type-safe access to the contained object.
|
||||||
|
*
|
||||||
|
* Throws libanyboost::bad_any_cast if the typeid of the requested
|
||||||
|
* ValueType does not match that of the contents of operand.
|
||||||
|
*
|
||||||
|
* @param operand target any object
|
||||||
|
*/
|
||||||
|
template<typename ValueType>
|
||||||
|
inline ValueType
|
||||||
|
any_cast(any &&operand)
|
||||||
|
{
|
||||||
|
static_assert(std::is_rvalue_reference<ValueType &&>::value
|
||||||
|
|| std::is_const<typename std::remove_reference<ValueType>::type>::value,
|
||||||
|
"any_cast shall not be used for getting nonconst references to "
|
||||||
|
"temporary objects");
|
||||||
|
|
||||||
|
return any_cast<ValueType>(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace reflection
|
||||||
|
|
||||||
|
}// namespace meta
|
||||||
|
|
||||||
|
#endif /* META_ANY_H */
|
60
runtime/apply.h
Normal file
60
runtime/apply.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef META_APPLY_H
|
||||||
|
#define META_APPLY_H
|
||||||
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace meta {
|
||||||
|
namespace reflection {
|
||||||
|
namespace detail {
|
||||||
|
template<int... Seq>
|
||||||
|
struct Sequence {};
|
||||||
|
|
||||||
|
template<int N, int... Seq>
|
||||||
|
struct MakeSeq : MakeSeq<N - 1, N - 1, Seq...> {};
|
||||||
|
|
||||||
|
template<int... Seq>
|
||||||
|
struct MakeSeq<0, Seq...> {
|
||||||
|
using type = Sequence<Seq...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ReturnT, typename Func, typename Tuple, int... Seq>
|
||||||
|
ReturnT
|
||||||
|
ApplyImpl(const Func &func, const Tuple &tuple, const Sequence<Seq...> &)
|
||||||
|
{
|
||||||
|
return std::bind(func, std::get<Seq>(tuple)...)();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VoidTag {};
|
||||||
|
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
template<typename ReturnT,
|
||||||
|
typename Func,
|
||||||
|
typename Tuple,
|
||||||
|
typename std::enable_if<!std::is_void<ReturnT>::value, ReturnT>::type * = nullptr>
|
||||||
|
ReturnT
|
||||||
|
apply(const Func &func, const Tuple &tuple)
|
||||||
|
{
|
||||||
|
return detail::ApplyImpl<ReturnT>(
|
||||||
|
func,
|
||||||
|
tuple,
|
||||||
|
typename detail::MakeSeq<std::tuple_size<Tuple>::value>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ReturnT = void,
|
||||||
|
typename Func,
|
||||||
|
typename Tuple,
|
||||||
|
typename std::enable_if<std::is_void<ReturnT>::value, detail::VoidTag>::type * = nullptr>
|
||||||
|
ReturnT
|
||||||
|
apply(const Func &func, const Tuple &tuple)
|
||||||
|
{
|
||||||
|
return detail::ApplyImpl<ReturnT>(
|
||||||
|
func,
|
||||||
|
tuple,
|
||||||
|
typename detail::MakeSeq<std::tuple_size<Tuple>::value>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace reflection
|
||||||
|
}// namespace meta
|
||||||
|
#endif// META_APPLY_H
|
@ -1,11 +1,67 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#ifndef META_RUNTIME_REFLECTION_H
|
#ifndef META_RUNTIME_REFLECTION_H
|
||||||
#define META_RUNTIME_REFLECTION_H
|
#define META_RUNTIME_REFLECTION_H
|
||||||
|
#include "any.h"
|
||||||
|
#include "apply.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <sled/log/log.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
|
namespace reflection {
|
||||||
|
namespace detail {
|
||||||
|
inline std::string
|
||||||
|
RemoveTypeSpace(const std::string &str, const std::string &chars = "[]()<>*&:")
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
// trim
|
||||||
|
{
|
||||||
|
result = str.substr(str.find_first_not_of(' '));
|
||||||
|
result = result.substr(0, result.find_last_not_of(' ') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove right space
|
||||||
|
for (size_t i = 0; i < result.size();) {
|
||||||
|
if (result[i] == ' ') {
|
||||||
|
bool left_is_chars = i > 0 && chars.find(result[i - 1]) != std::string::npos;
|
||||||
|
bool right_is_chars
|
||||||
|
= (i + 1 < str.size()) && chars.find(result[i + 1]) != std::string::npos;
|
||||||
|
if (left_is_chars || right_is_chars) {
|
||||||
|
result.erase(i, 1);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U = typename std::decay<T>::type>
|
||||||
|
std::string
|
||||||
|
PrettyNameImpl()
|
||||||
|
{
|
||||||
|
const std::string name = typeid(U).name();
|
||||||
|
std::string pretty_name = name;
|
||||||
|
int status = -4;
|
||||||
|
char *res = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
|
||||||
|
if (status == 0) { pretty_name = std::string(res); }
|
||||||
|
free(res);
|
||||||
|
return RemoveTypeSpace(pretty_name);
|
||||||
|
}
|
||||||
|
}// namespace detail
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline const std::string &
|
||||||
|
PrettyName()
|
||||||
|
{
|
||||||
|
static std::string pretty_name = detail::PrettyNameImpl<T>();
|
||||||
|
return pretty_name;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__REFLECTION_PARSER__)
|
#if defined(__REFLECTION_PARSER__)
|
||||||
#define META(...) __attribute__((annotate(#__VA_ARGS__)))
|
#define META(...) __attribute__((annotate(#__VA_ARGS__)))
|
||||||
@ -17,139 +73,119 @@ namespace meta {
|
|||||||
#define STRUCT(struct_name, ...) struct struct_name
|
#define STRUCT(struct_name, ...) struct struct_name
|
||||||
#endif// __REFLECTION_PARSER_
|
#endif// __REFLECTION_PARSER_
|
||||||
|
|
||||||
#define REFLECTION_BODY(class_name) \
|
#define META_REFLECTION_BODY(class_name) \
|
||||||
|
friend class meta::reflection::##class_name##_Method; \
|
||||||
|
friend class meta::reflection::##class_name##_Field; \
|
||||||
|
friend class meta::reflection::##class_name##_Constructor; \
|
||||||
|
friend class meta::reflection::##class_name##_Serializer;
|
||||||
|
|
||||||
|
class Constructor;
|
||||||
|
class Method;
|
||||||
|
class Field;
|
||||||
|
class Class;
|
||||||
|
|
||||||
|
class BaseType {
|
||||||
|
public:
|
||||||
|
BaseType(const std::string &name, std::shared_ptr<Class> parent) : name_(name), parent_(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~BaseType() = default;
|
||||||
|
|
||||||
|
std::string Name() const { return name_; }
|
||||||
|
|
||||||
|
std::shared_ptr<Class> Parent() const { return parent_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string name_;
|
||||||
|
std::shared_ptr<Class> parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Method : public BaseType {
|
||||||
|
public:
|
||||||
|
Method(const std::string &name, std::shared_ptr<Class> parent) : BaseType(name, parent) {}
|
||||||
|
|
||||||
|
~Method() override = default;
|
||||||
|
|
||||||
|
std::shared_ptr<Class> Parent() const { return parent_; }
|
||||||
|
|
||||||
|
template<typename ReturnT,
|
||||||
|
typename std::enable_if<!std::is_void<ReturnT>::value, ReturnT>::type * = nullptr,
|
||||||
|
typename... Params>
|
||||||
|
inline ReturnT InvokeAndCast(void *instance, Params &&...params) const
|
||||||
|
{
|
||||||
|
return ::meta::reflection::any_cast<ReturnT>(InvokeImpl(instance, {¶ms...}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ReturnT,
|
||||||
|
typename std::enable_if<std::is_void<ReturnT>::value, int>::type * = nullptr,
|
||||||
|
typename... Params>
|
||||||
|
inline void InvokeAndCast(void *instance, Params &&...params) const
|
||||||
|
{
|
||||||
|
::meta::reflection::any_cast<ReturnT>(InvokeImpl(instance, {¶ms...}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Params>
|
||||||
|
inline ::meta::reflection::any Invoke(void *instance, Params &&...params) const
|
||||||
|
{
|
||||||
|
return InvokeImpl(instance, {¶ms...});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ::meta::reflection::any InvokeImpl(void *instance,
|
||||||
|
const std::vector<void *> ¶ms) const
|
||||||
|
= 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Class> parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Field : public BaseType {
|
||||||
|
public:
|
||||||
|
Field(const std::string &name, std::shared_ptr<Class> parent) : BaseType(name, parent) {}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<!std::is_void<T>::value, T>::type * = nullptr>
|
||||||
|
inline T GetAndCast(void *instance) const
|
||||||
|
{
|
||||||
|
return ::meta::reflection::any_cast<T>(GetImpl(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
::meta::reflection::any Get(void *instance) const { return GetImpl(instance); }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void Set(void *instance, const T &value) const
|
||||||
|
{
|
||||||
|
SetImpl(instance, ::meta::reflection::any(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ::meta::reflection::any GetImpl(void *instance) const = 0;
|
||||||
|
virtual void SetImpl(void *instance, const ::meta::reflection::any &value) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Class : public BaseType {
|
||||||
|
public:
|
||||||
|
Class(const std::string &name, std::shared_ptr<Class> base_class = nullptr)
|
||||||
|
: BaseType(name, base_class)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REFLECTION_BODY() \
|
||||||
friend class meta::Serializer; \
|
friend class meta::Serializer; \
|
||||||
friend class meta::Class; \
|
friend class meta::Class; \
|
||||||
friend class meta::Method; \
|
friend class meta::Method; \
|
||||||
friend class meta::Field; \
|
friend class meta::Field; \
|
||||||
friend class meta::Constructor;
|
friend class meta::Constructor;
|
||||||
|
|
||||||
class TypeInfo {
|
|
||||||
public:
|
|
||||||
TypeInfo(const std::string &name) : name_(name) {}
|
|
||||||
|
|
||||||
virtual ~TypeInfo() = default;
|
|
||||||
std::string name() const;
|
|
||||||
|
|
||||||
virtual bool IsStatic() const = 0;
|
|
||||||
virtual bool IsConst() const = 0;
|
|
||||||
|
|
||||||
virtual bool IsWritable() const { return !IsConst(); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string name_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Class;
|
|
||||||
class Constructor;
|
|
||||||
class Field;
|
|
||||||
class Method;
|
|
||||||
|
|
||||||
class Class : public TypeInfo {
|
|
||||||
public:
|
|
||||||
Class(const std::string &name) : TypeInfo(name) {}
|
|
||||||
|
|
||||||
~Class() override = default;
|
|
||||||
Constructor GetConstructor(const std::string &name) const;
|
|
||||||
Field GetField(const std::string &name) const;
|
|
||||||
Method GetMethod(const std::string &name) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class Test {
|
|
||||||
* Test();
|
|
||||||
* Test(int val);
|
|
||||||
* Test(const Test & other);
|
|
||||||
* };
|
|
||||||
**/
|
|
||||||
class Constructor : public TypeInfo {
|
|
||||||
public:
|
|
||||||
Constructor(const std::string &name, const Class &clz) : TypeInfo(name), parent_(clz) {}
|
|
||||||
|
|
||||||
~Constructor() override = default;
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void *NewInstance(Args... args) const
|
|
||||||
{
|
|
||||||
return NewInstanceImpl({&args...});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void *NewInstanceImpl(const std::vector<void *> &args) const = 0;
|
|
||||||
const Class &parent_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Field : public TypeInfo {
|
|
||||||
public:
|
|
||||||
Field(const std::string &name, const Class &clz) : TypeInfo(name), parent_(clz) {}
|
|
||||||
|
|
||||||
~Field() override = default;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void Set(void *instance, T &&value) const
|
|
||||||
{
|
|
||||||
SetImpl(instance, &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
const T &Get(void *instance) const
|
|
||||||
{
|
|
||||||
return *static_cast<T *>(GetImpl(instance));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T &Get(void *instance) const
|
|
||||||
{
|
|
||||||
return *static_cast<T *>(GetImpl(instance));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void SetImpl(void *instance, void *value) const = 0;
|
|
||||||
virtual void *GetImpl(void *instance) const = 0;
|
|
||||||
|
|
||||||
const Class &parent_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Method : public TypeInfo {
|
|
||||||
public:
|
|
||||||
Method(const std::string &name, const Class &clz) : TypeInfo(name), parent_(clz) {}
|
|
||||||
|
|
||||||
~Method() override = default;
|
|
||||||
|
|
||||||
template<typename ReturnT,
|
|
||||||
typename std::enable_if<!std::is_void<ReturnT>::value, ReturnT>::type * = nullptr>
|
|
||||||
inline ReturnT InvokeProxy(void *instance, const std::vector<void *> &args) const
|
|
||||||
{
|
|
||||||
return *static_cast<ReturnT *>(InvokeImpl(instance, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void InvokeProxy(void *instance, const std::vector<void *> &args) const
|
|
||||||
{
|
|
||||||
InvokeImpl(instance, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ReturnT, typename... Args>
|
|
||||||
inline ReturnT Invoke(void *instance, Args... args)
|
|
||||||
{
|
|
||||||
return InvokeProxy<ReturnT>(instance, {&args...});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void Invoke(void *instance, Args... args)
|
|
||||||
{
|
|
||||||
InvokeProxy<void>(instance, {&args...});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void *InvokeImpl(void *instance, const std::vector<void *> &args) const = 0;
|
|
||||||
const Class &parent_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Reflectioin {
|
class Reflectioin {
|
||||||
public:
|
public:
|
||||||
static Reflectioin *Instance();
|
static Reflectioin *Instance();
|
||||||
~Reflectioin();
|
~Reflectioin();
|
||||||
|
std::shared_ptr<Class> GetClass();
|
||||||
|
// std::shared_ptr<Constructor> GetConstructor(std::shared_ptr<Class> clz);
|
||||||
|
std::shared_ptr<Method> GetMethod(std::shared_ptr<Class> clz);
|
||||||
|
std::shared_ptr<Field> GetField(std::shared_ptr<Class> clz);
|
||||||
};
|
};
|
||||||
|
}// namespace reflection
|
||||||
|
|
||||||
}// namespace meta
|
}// namespace meta
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "reflection.h"
|
#include "reflection.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
class TypeDef {};
|
||||||
|
|
||||||
CLASS(Test)
|
CLASS(Test)
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -17,6 +19,7 @@ public:
|
|||||||
int GetA() const { return a_; }
|
int GetA() const { return a_; }
|
||||||
|
|
||||||
int kk(int, void *);
|
int kk(int, void *);
|
||||||
|
TypeDef GetTypeDef() const { return type_def_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Void {};
|
struct Void {};
|
||||||
@ -29,6 +32,9 @@ private:
|
|||||||
|
|
||||||
const int b_;
|
const int b_;
|
||||||
int a_;
|
int a_;
|
||||||
|
|
||||||
|
META("TypeDef")
|
||||||
|
TypeDef type_def_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}// namespace test
|
}// namespace test
|
||||||
|
@ -19,12 +19,14 @@ public:
|
|||||||
CursorType(const CXType &handle);
|
CursorType(const CXType &handle);
|
||||||
|
|
||||||
std::string GetDisplayName(void) const;
|
std::string GetDisplayName(void) const;
|
||||||
|
bool IsConst(void) const;
|
||||||
|
|
||||||
|
// for Function type
|
||||||
int GetArgumentCount(void) const;
|
int GetArgumentCount(void) const;
|
||||||
CursorType GetArgument(unsigned index) const;
|
CursorType GetArgument(unsigned index) const;
|
||||||
CursorType GetCanonicalType(void) const;
|
CursorType GetCanonicalType(void) const;
|
||||||
Cursor GetDeclaration(void) const;
|
Cursor GetDeclaration(void) const;
|
||||||
CXTypeKind GetKind(void) const;
|
CXTypeKind GetKind(void) const;
|
||||||
bool IsConst(void) const;
|
|
||||||
|
|
||||||
CXType handle() const { return handle_; }
|
CXType handle() const { return handle_; }
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include <sled/cleanup.h>
|
||||||
#include <sled/log/log.h>
|
#include <sled/log/log.h>
|
||||||
#include <sled/strings/utils.h>
|
#include <sled/strings/utils.h>
|
||||||
|
|
||||||
@ -38,30 +39,29 @@ Parser::Parse(const std::string &file_name, std::vector<const char *> extra_args
|
|||||||
0,
|
0,
|
||||||
CXTranslationUnit_None);
|
CXTranslationUnit_None);
|
||||||
CXDiagnosticSet diagnostics = clang_getDiagnosticSetFromTU(translation_unit_);
|
CXDiagnosticSet diagnostics = clang_getDiagnosticSetFromTU(translation_unit_);
|
||||||
|
sled::Cleanup<> diagnostic_cleanup([&diagnostics] { clang_disposeDiagnosticSet(diagnostics); });
|
||||||
|
|
||||||
unsigned num_diagnostics = clang_getNumDiagnosticsInSet(diagnostics);
|
unsigned num_diagnostics = clang_getNumDiagnosticsInSet(diagnostics);
|
||||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
||||||
CXDiagnostic diagnostic = clang_getDiagnosticInSet(diagnostics, i);
|
CXDiagnostic diagnostic = clang_getDiagnosticInSet(diagnostics, i);
|
||||||
|
sled::Cleanup<> cleanup([&diagnostic] { clang_disposeDiagnostic(diagnostic); });
|
||||||
|
|
||||||
auto msg = ToString(clang_formatDiagnostic(diagnostic, CXDiagnostic_DisplaySourceLocation));
|
auto msg = ToString(clang_formatDiagnostic(diagnostic, CXDiagnostic_DisplaySourceLocation));
|
||||||
std::string filename;
|
std::string filename;
|
||||||
{
|
{
|
||||||
CXSourceLocation diagLoc = clang_getDiagnosticLocation(diagnostic);
|
CXSourceLocation diagLoc = clang_getDiagnosticLocation(diagnostic);
|
||||||
|
// ignore error from std
|
||||||
|
if (clang_Location_isInSystemHeader(diagLoc)) { continue; };
|
||||||
CXFile file;
|
CXFile file;
|
||||||
clang_getExpansionLocation(diagLoc, &file, nullptr, nullptr, nullptr);
|
clang_getExpansionLocation(diagLoc, &file, nullptr, nullptr, nullptr);
|
||||||
filename = ToString(clang_getFileName(file));
|
filename = ToString(clang_getFileName(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sled::EndsWith(filename, "reflection.h")) {
|
// ignore error for reflection.h
|
||||||
clang_disposeDiagnostic(diagnostic);
|
if (sled::EndsWith(filename, "reflection.h")) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
clang_disposeDiagnostic(diagnostic);
|
|
||||||
|
|
||||||
if (msg.find("error: use of undeclared identifier 'std'") == std::string::npos) {
|
|
||||||
LOGE("parser", "diagnostic: {}", msg);
|
LOGE("parser", "diagnostic: {}", msg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
clang_disposeDiagnosticSet(diagnostics);
|
|
||||||
|
|
||||||
return translation_unit_ != nullptr;
|
return translation_unit_ != nullptr;
|
||||||
}
|
}
|
||||||
|
@ -3,47 +3,88 @@
|
|||||||
#include "types/method.h"
|
#include "types/method.h"
|
||||||
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* class_name = "class_name",
|
||||||
|
* properties = []
|
||||||
|
* class_type = "ns1::ns2::class_name",
|
||||||
|
* fields = [
|
||||||
|
* ],
|
||||||
|
* methods = [
|
||||||
|
* {
|
||||||
|
* name = "method_name",
|
||||||
|
* properties = []
|
||||||
|
* is_const = false,
|
||||||
|
* return_type = "int",
|
||||||
|
* params = [
|
||||||
|
* {
|
||||||
|
* name = "arg_name1",
|
||||||
|
* type = "int",
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* name = "arg_name2",
|
||||||
|
* type = "void*",
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
**/
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseGenerator::GenClassRenderData(Class *clz, Mustache::data &class_def)
|
BaseGenerator::GenClassRenderData(Class *clz, inja::json &class_def)
|
||||||
{
|
{
|
||||||
class_def.set("class_name", clz->name());
|
class_def["class_name"] = clz->Name();
|
||||||
class_def.set("class_need_registry", true);
|
class_def["class_namespace"] = sled::StrJoin(clz->namespaces(), "::");
|
||||||
|
class_def["properties"] = inja::json::array();
|
||||||
|
for (auto &prop : clz->properties()) { class_def["properties"].push_back(prop); }
|
||||||
|
|
||||||
Mustache::data class_field_defines = Mustache::data::type::list;
|
inja::json methods = inja::json::array();
|
||||||
GenClassFieldRenderData(clz, class_field_defines);
|
inja::json fields = inja::json::array();
|
||||||
class_def.set("class_field_defines", class_field_defines);
|
|
||||||
|
|
||||||
Mustache::data class_method_defines = Mustache::data::type::list;
|
GenClassMethodRenderData(clz, methods);
|
||||||
GenClassMethodRenderData(clz, class_method_defines);
|
GenClassFieldRenderData(clz, fields);
|
||||||
class_def.set("class_method_defines", class_method_defines);
|
|
||||||
|
class_def["class_methods"] = methods;
|
||||||
|
class_def["class_fields"] = fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseGenerator::GenClassFieldRenderData(Class *clz, Mustache::data &field_defs)
|
BaseGenerator::GenClassConstructorRenderData(Class *clz, inja::json &constructor_defs)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
BaseGenerator::GenClassFieldRenderData(Class *clz, inja::json &field_defs)
|
||||||
{
|
{
|
||||||
for (auto &field : clz->fields()) {
|
for (auto &field : clz->fields()) {
|
||||||
Mustache::data field_def;
|
inja::json field_def;
|
||||||
field_def.set("class_field_name", field->name());
|
field_def["name"] = field->Name();
|
||||||
field_def.set("class_field_type", field->type_name());
|
field_def["is_const"] = field->IsConst();
|
||||||
field_def.set("class_field_display_name", field->cursor().GetDisplayName());
|
field_def["properties"] = inja::json::array();
|
||||||
if (field->type_name().find("std::vector") != std::string::npos) {
|
for (auto &prop : field->properties()) { field_def["properties"].push_back(prop); }
|
||||||
field_def.set("class_field_is_vector", true);
|
field_def["type"] = field->cursor().GetType().GetDisplayName();
|
||||||
} else {
|
|
||||||
field_def.set("class_field_is_vector", false);
|
|
||||||
}
|
|
||||||
field_defs.push_back(field_def);
|
field_defs.push_back(field_def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BaseGenerator::GenClassMethodRenderData(Class *clz, Mustache::data &method_defs)
|
BaseGenerator::GenClassMethodRenderData(Class *clz, inja::json &method_defs)
|
||||||
{
|
{
|
||||||
for (auto &method : clz->methods()) {
|
for (auto &method : clz->methods()) {
|
||||||
Mustache::data method_def;
|
inja::json method_def;
|
||||||
method_def.set("class_method_name", method->name());
|
method_def["name"] = method->Name();
|
||||||
method_def.set("class_method_return_type", method->return_type().GetDisplayName());
|
method_def["properties"] = inja::json::array();
|
||||||
method_def.set("class_method_display_name", method->cursor().GetDisplayName());
|
for (auto &prop : method->properties()) { method_def["properties"].push_back(prop); }
|
||||||
|
method_def["is_const"] = method->IsConst();
|
||||||
|
method_def["return_type"] = method->return_type().GetDisplayName();
|
||||||
|
inja::json params = inja::json::array();
|
||||||
|
for (auto ¶m : method->arguments()) {
|
||||||
|
inja::json param_def;
|
||||||
|
param_def["name"] = "";
|
||||||
|
param_def["type"] = param.GetDisplayName();
|
||||||
|
params.push_back(param_def);
|
||||||
|
}
|
||||||
|
method_def["params"] = params;
|
||||||
method_defs.push_back(method_def);
|
method_defs.push_back(method_def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,20 @@
|
|||||||
#ifndef META_GENERATORS_BASE_GENERATOR_H
|
#ifndef META_GENERATORS_BASE_GENERATOR_H
|
||||||
#define META_GENERATORS_BASE_GENERATOR_H
|
#define META_GENERATORS_BASE_GENERATOR_H
|
||||||
|
|
||||||
|
#include "inja.hpp"
|
||||||
#include "types/class.h"
|
#include "types/class.h"
|
||||||
#include <mustache.hpp>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
using namespace kainjow;
|
|
||||||
namespace Mustache = kainjow::mustache;
|
|
||||||
|
|
||||||
class BaseGenerator {
|
class BaseGenerator {
|
||||||
public:
|
public:
|
||||||
virtual ~BaseGenerator() = default;
|
virtual ~BaseGenerator() = default;
|
||||||
virtual void Generate() = 0;
|
virtual void Generate() = 0;
|
||||||
virtual void GenClassRenderData(Class *clz, Mustache::data &class_def);
|
virtual void GenClassRenderData(Class *clz, inja::json &class_def);
|
||||||
virtual void GenClassFieldRenderData(Class *clz, Mustache::data &field_defs);
|
virtual void GenClassConstructorRenderData(Class *clz, inja::json &constructor_defs);
|
||||||
virtual void GenClassMethodRenderData(Class *clz, Mustache::data &method_defs);
|
virtual void GenClassFieldRenderData(Class *clz, inja::json &field_defs);
|
||||||
|
virtual void GenClassMethodRenderData(Class *clz, inja::json &method_defs);
|
||||||
};
|
};
|
||||||
}// namespace meta
|
}// namespace meta
|
||||||
|
|
||||||
|
122
src/main.cc
122
src/main.cc
@ -1,15 +1,132 @@
|
|||||||
|
#include "generators/base_generator.h"
|
||||||
|
#include "reflection.h"
|
||||||
#include "registry.h"
|
#include "registry.h"
|
||||||
#include "types/class.h"
|
#include "types/class.h"
|
||||||
#include "clang/parser.h"
|
#include "clang/parser.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <sled/any.h>
|
||||||
|
#include <sled/apply.h>
|
||||||
#include <sled/filesystem/path.h>
|
#include <sled/filesystem/path.h>
|
||||||
#include <sled/log/log.h>
|
#include <sled/log/log.h>
|
||||||
#include <sled/strings/utils.h>
|
#include <sled/strings/utils.h>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
const char *kTag = "main";
|
const char *kTag = "main";
|
||||||
|
|
||||||
|
class Generator : public meta::BaseGenerator {
|
||||||
|
public:
|
||||||
|
Generator(const std::string &template_path)
|
||||||
|
{
|
||||||
|
std::fstream in(template_path, std::ios::in);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << in.rdbuf();
|
||||||
|
template_ = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Generator() override = default;
|
||||||
|
|
||||||
|
void Generate() override
|
||||||
|
{
|
||||||
|
for (auto &clz : meta::Registry::Instance()->classes()) {
|
||||||
|
inja::json class_def;
|
||||||
|
GenClassRenderData(clz, class_def);
|
||||||
|
inja::Environment env;
|
||||||
|
env.set_trim_blocks(true);
|
||||||
|
env.set_lstrip_blocks(true);
|
||||||
|
try {
|
||||||
|
LOGV(kTag, "data={}", class_def.dump());
|
||||||
|
LOGV(kTag, "class_def={}", env.render(template_, class_def));
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LOGE(kTag, "error: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string template_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// template<typename ClassT, typename ReturnT = void, typename... Args>
|
||||||
|
// std::function<void(sled::any)>
|
||||||
|
// MemberFunctionWrapper(void (ClassT::*func)(Args...))
|
||||||
|
// {
|
||||||
|
// return [=](sled::any obj_args) {
|
||||||
|
// using tuple = ::std::tuple<ClassT *, Args...>;
|
||||||
|
// // ::std::tuple<ClassT *, Args...> *pTuple = ::sled::any_cast<tuple *>(obj_args);
|
||||||
|
// auto tuple_ptr = ::sled::any_cast<tuple *>(obj_args);
|
||||||
|
// sled::apply(func, *tuple_ptr);
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
template<typename ClassT, typename ReturnT, typename... Args>
|
||||||
|
std::function<ReturnT(sled::any)>
|
||||||
|
MemberFunctionWrapper(ReturnT (ClassT::*func)(Args...))
|
||||||
|
{
|
||||||
|
return [=](sled::any obj_args) {
|
||||||
|
using tuple = ::std::tuple<ClassT *, Args...>;
|
||||||
|
// ::std::tuple<ClassT *, Args...> *pTuple = ::sled::any_cast<tuple *>(obj_args);
|
||||||
|
auto tuple_ptr = ::sled::any_cast<tuple *>(obj_args);
|
||||||
|
return sled::apply<ReturnT>(func, *tuple_ptr);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public:
|
||||||
|
void Show(int a, double b, std::string c) { LOGV(kTag, "a={}, b={}, c={}", a, b, c); }
|
||||||
|
|
||||||
|
// int Show(int a, int b, const std::string &c)
|
||||||
|
// {
|
||||||
|
// LOGV(kTag, "a={}, b={}, c={}", a, b, c);
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Adder {
|
||||||
|
public:
|
||||||
|
int Add(int a, int b) { return a + b; }
|
||||||
|
friend class meta::reflection::Method;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add(int, int)
|
||||||
|
class AdderMethod : public meta::reflection::Method {
|
||||||
|
public:
|
||||||
|
AdderMethod(const std::string &name, std::shared_ptr<meta::reflection::Class> parent)
|
||||||
|
: meta::reflection::Method(name, parent)
|
||||||
|
{
|
||||||
|
fun_ = MemberFunctionWrapper<Adder, int, int, int>(&Adder::Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Add(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
meta::reflection::any InvokeImpl(void *instance,
|
||||||
|
const std::vector<void *> ¶ms) const override
|
||||||
|
{
|
||||||
|
Adder *adder = reinterpret_cast<Adder *>(instance);
|
||||||
|
using tuple = std::tuple<int, int>;
|
||||||
|
auto t = std::make_tuple(adder,
|
||||||
|
*reinterpret_cast<int *>(params[0]),
|
||||||
|
*reinterpret_cast<int *>(params[1]));
|
||||||
|
return fun_(&t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<int(sled::any)> fun_;
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
auto clz = std::make_shared<meta::reflection::Class>("Test");
|
||||||
|
class Adder adder;
|
||||||
|
meta::reflection::Method *method = new AdderMethod("Add", clz);
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
LOGV("", "{}+{}={}", i, i, meta::reflection::any_cast<int>(method->Invoke(&adder, i, i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void (Test::*p)(int, double, std::string) = &Test::Show;
|
||||||
|
|
||||||
|
LOGV(kTag, "{}", meta::reflection::PrettyName<meta::Registry>());
|
||||||
// LOGI(kTag, "cwd={}", sled::Path::Current().ToString());
|
// LOGI(kTag, "cwd={}", sled::Path::Current().ToString());
|
||||||
meta::Parser parser;
|
meta::Parser parser;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -34,5 +151,10 @@ main(int argc, char *argv[])
|
|||||||
LOGI(kTag, "{}\n{}", meta::Registry::Instance()->GetQualifiedName(clz), clz->ToString());
|
LOGI(kTag, "{}\n{}", meta::Registry::Instance()->GetQualifiedName(clz), clz->ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Generator generator1("template/class_proxy_include.inja");
|
||||||
|
generator1.Generate();
|
||||||
|
Generator generator2("template/class_proxy_source.inja");
|
||||||
|
generator2.Generate();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ void
|
|||||||
Registry::AddClass(Class *cls)
|
Registry::AddClass(Class *cls)
|
||||||
{
|
{
|
||||||
if (!cls->ShouldGenerate()) { return; }
|
if (!cls->ShouldGenerate()) { return; }
|
||||||
const std::string key = sled::StrJoin(cls->namespaces(), "::") + "::" + cls->name();
|
const std::string key = sled::StrJoin(cls->namespaces(), "::") + "::" + cls->Name();
|
||||||
if (class_by_qualified_name_.find(key) == class_by_qualified_name_.end()) {
|
if (class_by_qualified_name_.find(key) == class_by_qualified_name_.end()) {
|
||||||
class_by_qualified_name_[key] = cls;
|
class_by_qualified_name_[key] = cls;
|
||||||
qualified_name_by_class_[cls] = key;
|
qualified_name_by_class_[cls] = key;
|
||||||
|
@ -19,11 +19,11 @@ BaseType::BaseType(const Cursor &cursor, const std::vector<std::string> &namespa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
// std::string
|
||||||
BaseType::type_name() const
|
// BaseType::type_name() const
|
||||||
{
|
// {
|
||||||
return cursor_.GetType().GetDisplayName();
|
// return sled::StrJoin(namespaces_, "::") + "::" + name();
|
||||||
}
|
// }
|
||||||
|
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
BaseType::GetProperties(const Cursor &annotation_cursor)
|
BaseType::GetProperties(const Cursor &annotation_cursor)
|
||||||
@ -38,7 +38,7 @@ std::string
|
|||||||
BaseType::ToString() const
|
BaseType::ToString() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << name();
|
ss << Name();
|
||||||
if (!properties_.empty()) { ss << " /*" << sled::StrJoin(properties(), ",") << "*/"; }
|
if (!properties_.empty()) { ss << " /*" << sled::StrJoin(properties(), ",") << "*/"; }
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,28 @@
|
|||||||
#ifndef META_TYPES_TYPE_INTERFACE_H
|
#ifndef META_TYPES_TYPE_INTERFACE_H
|
||||||
#define META_TYPES_TYPE_INTERFACE_H
|
#define META_TYPES_TYPE_INTERFACE_H
|
||||||
#include "clang/cursor.h"
|
#include "clang/cursor.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
|
/**
|
||||||
|
* Class (class test)
|
||||||
|
* - is_const = false
|
||||||
|
* - name = test
|
||||||
|
* - type_name = test
|
||||||
|
* Method (int foo(int a, int b) const)
|
||||||
|
* - is_const = true
|
||||||
|
* - name = foo
|
||||||
|
* - type = int(int, int) const
|
||||||
|
* - type_name = int(int, int) const
|
||||||
|
* - return_type = int
|
||||||
|
* - arguments = [int, int]
|
||||||
|
* - argument_count = 2
|
||||||
|
* Field (const int foo)
|
||||||
|
* - is_const = true
|
||||||
|
* - name = foo
|
||||||
|
* - type const int
|
||||||
|
* - type_name = const int
|
||||||
|
**/
|
||||||
class BaseType {
|
class BaseType {
|
||||||
public:
|
public:
|
||||||
static std::vector<std::string> GetProperties(const Cursor &annotation_cursor);
|
static std::vector<std::string> GetProperties(const Cursor &annotation_cursor);
|
||||||
@ -11,22 +31,27 @@ public:
|
|||||||
BaseType(const Cursor &cursor, const std::vector<std::string> &namespaces);
|
BaseType(const Cursor &cursor, const std::vector<std::string> &namespaces);
|
||||||
virtual ~BaseType() = default;
|
virtual ~BaseType() = default;
|
||||||
|
|
||||||
virtual std::string name() const { return cursor_.GetSpelling(); }
|
virtual bool IsConst() const { return false; }
|
||||||
|
|
||||||
virtual std::string type_name() const;
|
virtual std::string Name() const { return cursor_.GetSpelling(); }
|
||||||
|
|
||||||
|
virtual std::string TypeName() const = 0;
|
||||||
|
|
||||||
virtual std::string ToString() const;
|
virtual std::string ToString() const;
|
||||||
|
|
||||||
virtual bool ShouldGenerate() const { return !properties_.empty(); }
|
virtual bool ShouldGenerate() const { return !properties_.empty(); }
|
||||||
|
|
||||||
bool IsPublic() const { return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXPublic; }
|
inline bool IsPublic() const
|
||||||
|
{
|
||||||
|
return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXPublic;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsPrivate() const
|
inline bool IsPrivate() const
|
||||||
{
|
{
|
||||||
return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXPrivate;
|
return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsProtected() const
|
inline bool IsProtected() const
|
||||||
{
|
{
|
||||||
return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXProtected;
|
return clang_getCXXAccessSpecifier(cursor_.handle()) == CX_CXXProtected;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ Class::Class(const Cursor &cursor, const std::vector<std::string> &namespaces)
|
|||||||
for (auto &child : cursor.GetChildren()) {
|
for (auto &child : cursor.GetChildren()) {
|
||||||
switch (child.GetKind()) {
|
switch (child.GetKind()) {
|
||||||
case CXCursor_Constructor:
|
case CXCursor_Constructor:
|
||||||
|
constructors_.push_back(new Method(child, namespaces_, *this));
|
||||||
|
should_generate_ = should_generate_ || constructors_.back()->ShouldGenerate();
|
||||||
break;
|
break;
|
||||||
case CXCursor_CXXMethod:
|
case CXCursor_CXXMethod:
|
||||||
methods_.push_back(new Method(child, namespaces_, *this));
|
methods_.push_back(new Method(child, namespaces_, *this));
|
||||||
@ -69,4 +71,10 @@ Class::ToString() const
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Class::TypeName() const
|
||||||
|
{
|
||||||
|
return sled::StrJoin(namespaces_, "::") + "::" + Name();
|
||||||
|
}
|
||||||
|
|
||||||
}// namespace meta
|
}// namespace meta
|
||||||
|
@ -17,14 +17,18 @@ public:
|
|||||||
Class(const Cursor &cursor, const std::vector<std::string> &namespaces);
|
Class(const Cursor &cursor, const std::vector<std::string> &namespaces);
|
||||||
~Class() override;
|
~Class() override;
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
|
std::string TypeName() const override;
|
||||||
|
|
||||||
bool ShouldGenerate() const override { return should_generate_; }
|
bool ShouldGenerate() const override { return should_generate_; }
|
||||||
|
|
||||||
|
const std::vector<Method *> &constructors() const { return constructors_; }
|
||||||
|
|
||||||
const std::vector<Field *> &fields() const { return fields_; }
|
const std::vector<Field *> &fields() const { return fields_; }
|
||||||
|
|
||||||
const std::vector<Method *> &methods() const { return methods_; }
|
const std::vector<Method *> &methods() const { return methods_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<Method *> constructors_;
|
||||||
std::vector<Field *> fields_;
|
std::vector<Field *> fields_;
|
||||||
std::vector<Method *> methods_;
|
std::vector<Method *> methods_;
|
||||||
bool should_generate_;
|
bool should_generate_;
|
||||||
|
@ -19,7 +19,13 @@ Field::Field(const Cursor &cursor, const std::vector<std::string> &namespaces, c
|
|||||||
std::string
|
std::string
|
||||||
Field::ToString() const
|
Field::ToString() const
|
||||||
{
|
{
|
||||||
return type_name() + " " + BaseType::ToString();
|
return TypeName() + " " + BaseType::ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Field::TypeName() const
|
||||||
|
{
|
||||||
|
return cursor().GetType().GetDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}// namespace meta
|
}// namespace meta
|
||||||
|
@ -13,6 +13,7 @@ public:
|
|||||||
~Field() override = default;
|
~Field() override = default;
|
||||||
|
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
|
std::string TypeName() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Class &parent_;
|
const Class &parent_;
|
||||||
|
@ -21,7 +21,7 @@ Method::ToString() const
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
// ss << type_name();
|
// ss << type_name();
|
||||||
ss << return_type_.GetDisplayName() << " " << name() << "(";
|
ss << return_type_.GetDisplayName() << " " << Name() << "(";
|
||||||
for (unsigned i = 0; i < arguments_.size(); ++i) {
|
for (unsigned i = 0; i < arguments_.size(); ++i) {
|
||||||
ss << arguments_[i].GetDisplayName();
|
ss << arguments_[i].GetDisplayName();
|
||||||
if (i < arguments_.size() - 1) { ss << ", "; }
|
if (i < arguments_.size() - 1) { ss << ", "; }
|
||||||
@ -33,6 +33,13 @@ Method::ToString() const
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Method::TypeName() const
|
||||||
|
{
|
||||||
|
// return sled::StrJoin(namespaces_, "::") + "::" + name();
|
||||||
|
return cursor().GetType().GetDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Method::IsConst() const
|
Method::IsConst() const
|
||||||
{
|
{
|
||||||
|
@ -11,11 +11,15 @@ public:
|
|||||||
~Method() override = default;
|
~Method() override = default;
|
||||||
|
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
|
std::string TypeName() const override;
|
||||||
|
|
||||||
bool IsConst() const;
|
bool IsConst() const override;
|
||||||
CursorType return_type() const;
|
|
||||||
CursorType argument(unsigned index) const;
|
CursorType return_type() const { return return_type_; }
|
||||||
std::vector<CursorType> arguments() const;
|
|
||||||
|
CursorType argument(unsigned index) const { return arguments_[index]; }
|
||||||
|
|
||||||
|
std::vector<CursorType> arguments() const { return arguments_; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Class &parent_;
|
const Class &parent_;
|
||||||
|
26
src/utils.h
26
src/utils.h
@ -2,13 +2,37 @@
|
|||||||
#ifndef META_UTILS_H
|
#ifndef META_UTILS_H
|
||||||
#define META_UTILS_H
|
#define META_UTILS_H
|
||||||
#include <clang-c/Index.h>
|
#include <clang-c/Index.h>
|
||||||
|
#include <sled/strings/utils.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace meta {
|
namespace meta {
|
||||||
inline std::string ToString(const CXString& str) {
|
inline std::string
|
||||||
|
ToString(const CXString &str)
|
||||||
|
{
|
||||||
std::string result = clang_getCString(str);
|
std::string result = clang_getCString(str);
|
||||||
clang_disposeString(str);
|
clang_disposeString(str);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
RemoveTypeSpace(const std::string &str, const std::string &chars = "[]()<>*&:")
|
||||||
|
{
|
||||||
|
std::string res = sled::Trim(str);
|
||||||
|
// remove right space
|
||||||
|
for (size_t i = 0; i < str.size();) {
|
||||||
|
if (str[i] == ' ') {
|
||||||
|
bool left_is_chars = i > 0 && chars.find(str[i - 1]) != std::string::npos;
|
||||||
|
bool right_is_chars
|
||||||
|
= (i + 1 < str.size()) && chars.find(str[i + 1]) != std::string::npos;
|
||||||
|
if (left_is_chars || right_is_chars) {
|
||||||
|
res.erase(i, 1);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace meta
|
||||||
#endif// META_UTILS_H
|
#endif// META_UTILS_H
|
||||||
|
34
template/class_proxy_include.inja
Normal file
34
template/class_proxy_include.inja
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef META_GEN_{{class_name}}_PROXY_H
|
||||||
|
#define META_GEN_{{class_name}}_PROXY_H
|
||||||
|
|
||||||
|
#include "runtime/reflection.h"
|
||||||
|
|
||||||
|
namespace meta {
|
||||||
|
namespace reflection {
|
||||||
|
|
||||||
|
class {{class_name}}_Class : public ::meta::reflection::Class {
|
||||||
|
};
|
||||||
|
|
||||||
|
class {{class_name}}_Field : public ::meta::reflection::Field {
|
||||||
|
public:
|
||||||
|
{{class_name}}_Field(const std::string& name, std::shared_ptr<::meta::reflection::Class> parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
::meta::reflection::any GetImpl(void* instance) const override;
|
||||||
|
void SetImpl(void* instance, const ::meta::reflection::any& value) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class {{class_name}}_Method : public ::meta::reflection::Method {
|
||||||
|
public:
|
||||||
|
{{class_name}}_Method(const std::string& name, std::shared_ptr<::meta::reflection::Class> parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
::meta::reflection::any InvokeImpl(void* instance, const std::vector<void *> ¶ms) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace reflection
|
||||||
|
} // namespace meta
|
||||||
|
|
||||||
|
#endif // META_GEN_{{class_name}}_PROXY_H
|
||||||
|
|
78
template/class_proxy_source.inja
Normal file
78
template/class_proxy_source.inja
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include "{{class_name}}.gen.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace meta {
|
||||||
|
namespace reflection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field
|
||||||
|
**/
|
||||||
|
|
||||||
|
{{class_name}}_Field::{{class_name}}_Field(
|
||||||
|
const std::string& name,
|
||||||
|
std::shared_ptr<::meta::reflection::Class> parent)
|
||||||
|
: ::meta::reflection::Field(name, parent) {}
|
||||||
|
|
||||||
|
::meta::reflection::any GetImpl(void* instance) const {
|
||||||
|
::{{class_namespace}}::{{class_name}}* obj = (::{{class_namespace}}::{{class_name}}*)instance;
|
||||||
|
|
||||||
|
{% for field in class_fields %}
|
||||||
|
if (strcmp(name().c_str(), "{{field.name}}") == 0) { return obj->{{field.name}}; }
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetImpl(void* instance, const ::meta::reflection::any& value) const {
|
||||||
|
::{{class_namespace}}::{{class_name}}* obj = (::{{class_namespace}}::{{class_name}}*)instance;
|
||||||
|
{% for field in class_fields %}
|
||||||
|
{% if not field.is_const %}
|
||||||
|
if(strcmp(name().c_str(), "{{field.name}}") == 0) { obj->{{field.name}} = value.as<{{field.type}}>();}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method
|
||||||
|
**/
|
||||||
|
{{class_name}}_Method::{{class_name}}_Method(
|
||||||
|
const std::string& name,
|
||||||
|
std::shared_ptr<::meta::reflection::Class> parent)
|
||||||
|
: ::meta::reflection::Method(name, parent) {}
|
||||||
|
|
||||||
|
::meta::reflection::any InvokeImpl(void* instance, const std::vector<void *> ¶ms) const {
|
||||||
|
::{{class_namespace}}::{{class_name}}* obj = (::{{class_namespace}}::{{class_name}}*)instance;
|
||||||
|
|
||||||
|
{% for method in class_methods %}
|
||||||
|
{# start if(, else if(, #}
|
||||||
|
{% if loop.is_first %}
|
||||||
|
if (strcmp(name.c_str(), "{{method.name}}") == 0) {
|
||||||
|
{% else %}
|
||||||
|
} else if (strcmp(name.c_str(), "{{method.name}}") == 0) {
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if method.return_type != "void" %}
|
||||||
|
return obj->{{method.name}}(
|
||||||
|
{% for param in method.params%}
|
||||||
|
*({{param.type}}*)params[{{loop.index}}]{% if not loop.is_last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
);
|
||||||
|
{% else %}
|
||||||
|
obj->{{method.name}}(
|
||||||
|
{% for param in method.params%}
|
||||||
|
*({{param.type}}*)params[{{loop.index}}]{% if not loop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if loop.is_last %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reflection
|
||||||
|
} // namespace meta
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user