feat support reflect properties

This commit is contained in:
tqcq 2024-03-03 17:19:41 +08:00
parent 0e1b34934d
commit 35ad59ab7a
25 changed files with 585 additions and 142 deletions

View File

@ -33,7 +33,6 @@ BraceWrapping:
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true

2
3rdparty/sled vendored

@ -1 +1 @@
Subproject commit 4b68474fe58fb3af754f93916438bc8d2ba6aac1
Subproject commit d048a2676c4194e19934e0263aeb54928bbd68a6

View File

@ -12,6 +12,11 @@ add_executable(meta
src/clang/cursor.cc
src/clang/cursor_type.cc
src/clang/parser.cc
src/types/base_type.cc
src/types/class.cc
src/types/field.cc
src/types/method.cc
src/registry.cc
)
### add clang

View File

@ -1,11 +0,0 @@
#include "reflection.h"
namespace meta {
std::atomic<int> next_type_id{0};
void
NoOp(void *)
{}
Object::Object() : id_(GetTypeId<void>()), data_(nullptr), deleter_(NoOp) {}
}// namespace meta

View File

@ -1,60 +1,155 @@
#pragma once
#ifndef META_RUNTIME_REFLECTION_H
#define META_RUNTIME_REFLECTION_H
#include <atomic>
#include <functional>
#include <sled/any.h>
#include <string>
#include <typeindex>
#include <type_traits>
#include <vector>
namespace meta {
#define __REFLECTION_PARSER_
#if defined(__REFLECTION_PARSER_)
#define CLASS(class_name, ...) \
class __attribute__((annotate(#__VA_ARGS__))) class_name
#define STRUCT(struct_name, ...) \
struct __attribute__((annotate(#__VA_ARGS__))) struct_name
#if defined(__REFLECTION_PARSER__)
#define META(...) __attribute__((annotate(#__VA_ARGS__)))
#define CLASS(class_name, ...) class __attribute__((annotate(#__VA_ARGS__))) class_name
#define STRUCT(struct_name, ...) struct __attribute__((annotate(#__VA_ARGS__))) struct_name
#else
#define META(...)
#define CLASS(class_name, ...) class class_name
#define STRUCT(struct_name, ...) struct struct_name
#endif// __REFLECTION_PARSER_
#define REFLECTION_BODY(class_name)
#define REFLECTION_BODY(class_name) \
friend class meta::Serializer; \
friend class meta::Class; \
friend class meta::Method; \
friend class meta::Field; \
friend class meta::Constructor;
#define REFLECT_TYPE(class_name)
class TypeInterface {
class TypeInfo {
public:
virtual std::string name() const = 0;
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 Method : public TypeInterface {
class Class : public TypeInfo {
public:
virtual std::string name() = 0;
Class(const std::string &name) : TypeInfo(name) {}
template<typename ReturnT, typename... Args>
ReturnT Invoke(void *instance, Args... args) const
{
std::vector<sled::any> arguments = {args...};
return sled::any_cast<ReturnT>(
std::forward<sled::any>(Invoke(instance, arguments)));
}
~Class() override = default;
Constructor GetConstructor(const std::string &name) const;
Field GetField(const std::string &name) const;
Method GetMethod(const std::string &name) const;
};
sled::any Invoke(void *instance, std::vector<sled::any> &args) 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 InvokeImpl(instance, args);
return NewInstanceImpl({&args...});
}
protected:
virtual sled::any InvokeImpl(void *instance,
std::vector<sled::any> &args) const = 0;
virtual void *NewInstanceImpl(const std::vector<void *> &args) const = 0;
const Class &parent_;
};
class Field;
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 {
public:
static Reflectioin *Instance();
~Reflectioin();
};
}// namespace meta

33
runtime/test.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#ifndef META_RUNTIME_TEST_H
#define META_RUNTIME_TEST_H
#include "reflection.h"
namespace test {
CLASS(Test)
{
public:
META("Constructor")
Test() : a_(0) {}
Test(int a) : a_(a) {}
META("Test")
int GetA() const { return a_; }
private:
struct Void {};
STRUCT(VoidMeta, "VoidMeta")
{
META("Enable")
int a;
};
int a_;
};
}// namespace test
#endif// META_RUNTIME_TEST_H

View File

@ -10,6 +10,12 @@ Cursor::GetKind(void)
return handle_.kind;
}
CXVisibilityKind
Cursor::GetVisibility(void)
{
return clang_getCursorVisibility(handle_);
}
std::string
Cursor::GetSpelling(void) const
{

View File

@ -14,6 +14,7 @@ public:
Cursor(const CXCursor &handle);
CXCursorKind GetKind(void);
CXVisibilityKind GetVisibility(void);
std::string GetSpelling(void) const;
std::string GetDisplayName(void) const;
std::string GetSourceFile(void) const;
@ -22,6 +23,8 @@ public:
List GetChildren(void) const;
void VisitChildren(Visitor visitor, void *data) const;
CXCursor handle() const { return handle_; }
private:
CXCursor handle_;
};

View File

@ -1,5 +1,7 @@
#include "parser.h"
#include "utils.h"
#include <sled/log/log.h>
#include <sled/strings/utils.h>
namespace meta {
Parser::Parser() : index_(nullptr), translation_unit_(nullptr) {}
@ -13,10 +15,8 @@ Parser::~Parser()
}
bool
Parser::Parse(const std::string &file_name)
Parser::Parse(const std::string &file_name, std::vector<const char *> extra_args)
{
const char *args[] = {"-std=c++11"};
if (translation_unit_) {
clang_disposeTranslationUnit(translation_unit_);
translation_unit_ = nullptr;
@ -27,14 +27,42 @@ Parser::Parse(const std::string &file_name)
index_ = nullptr;
}
extra_args.insert(extra_args.end(), kDefaultArguments.begin(), kDefaultArguments.end());
index_ = clang_createIndex(0, 0);
translation_unit_ = clang_parseTranslationUnit(index_,
file_name.c_str(),
kDefaultArguments.data(),
kDefaultArguments.size(),
extra_args.data(),
extra_args.size(),
nullptr,
0,
CXTranslationUnit_None);
CXDiagnosticSet diagnostics = clang_getDiagnosticSetFromTU(translation_unit_);
unsigned num_diagnostics = clang_getNumDiagnosticsInSet(diagnostics);
for (unsigned i = 0; i < num_diagnostics; ++i) {
CXDiagnostic diagnostic = clang_getDiagnosticInSet(diagnostics, i);
auto msg = ToString(clang_formatDiagnostic(diagnostic, CXDiagnostic_DisplaySourceLocation));
std::string filename;
{
CXSourceLocation diagLoc = clang_getDiagnosticLocation(diagnostic);
CXFile file;
clang_getExpansionLocation(diagLoc, &file, nullptr, nullptr, nullptr);
filename = ToString(clang_getFileName(file));
}
if (sled::EndsWith(filename, "reflection.h")) {
clang_disposeDiagnostic(diagnostic);
continue;
}
clang_disposeDiagnostic(diagnostic);
if (msg.find("error: use of undeclared identifier 'std'") == std::string::npos) {
LOGE("parser", "diagnostic: {}", msg);
}
}
clang_disposeDiagnosticSet(diagnostics);
return translation_unit_ != nullptr;
}
@ -44,5 +72,4 @@ Parser::GetCursor(void) const
ASSERT(translation_unit_, "translation_unit_ is nullptr");
return clang_getTranslationUnitCursor(translation_unit_);
};
}// namespace meta

View File

@ -12,25 +12,33 @@ public:
Parser();
~Parser();
bool Parse(const std::string &file_name);
bool Parse(const std::string &file_name, std::vector<const char *> extra_args = {});
Cursor GetCursor(void) const;
void AppendFileUniqueIDForIgnoreError(const CXFileUniqueID &id)
{
file_unique_ids_.push_back(id);
}
private:
CXIndex index_;
CXTranslationUnit translation_unit_;
private:
std::vector<const char *> kDefaultArguments = {{"-x",
"c++",
"-std=c++11",
"-D__REFLECTION_PARSER__",
"-DNDEBUG",
"-D__clang__",
"-w",
"-MG",
"-M",
"-ferror-limit=0",
"-oclangLog.txt"}};
std::vector<const char *> kDefaultArguments = {{
"-x",
"c++",
"-std=c++11",
"-D__REFLECTION_PARSER__",
"-DNDEBUG",
"-D__clang__",
"-w",
"-MG",
"-M",
"-ferror-limit=0",
"-oclangLog.txt",
}};
std::vector<CXFileUniqueID> file_unique_ids_;
};
}// namespace meta

View File

@ -1,15 +0,0 @@
#pragma once
#ifndef META_LANGUAGE_TYPES_FIELD_H
#define META_LANGUAGE_TYPES_FIELD_H
#include "type.h"
namespace meta {
class Field : public TypeInfo {
public:
virtual bool IsReadable() const;
virtual bool IsWritable() const;
virtual void *GetValue(void *instance) const;
virtual void SetValue(void *instance, void *value);
};
}// namespace meta
#endif// META_LANGUAGE_TYPES_FIELD_H

View File

@ -1,14 +0,0 @@
#pragma once
#ifndef META_LANGUAGE_TYPES_TYPE_H
#define META_LANGUAGE_TYPES_TYPE_H
#include <string>
class TypeInfo {
public:
virtual ~TypeInfo() = default;
virtual std::string name() const = 0;
virtual std::string qualified_name() const = 0;
};
#endif// META_LANGUAGE_TYPES_TYPE_H

View File

@ -1,28 +1,38 @@
#include "registry.h"
#include "types/class.h"
#include "types/field.h"
#include "clang/parser.h"
#include <sled/filesystem/path.h>
#include <sled/log/log.h>
#include <sled/strings/utils.h>
const char *kTag = "main";
int
main(int argc, char *argv[])
{
LOGI(kTag, "");
LOGI(kTag, "cwd={}", sled::Path::Current().ToString());
meta::Parser parser;
int i = 0;
for (i = 1; i < argc; i++) {
if (strncmp("--", argv[i], 2) == 0) {
i++;
break;
}
}
parser.Parse("/tmp/main.cc");
// if not find --, then start from 1
if (i == argc) { i = 1; }
std::vector<const char *> extra_args(argv + i, argv + argc);
parser.Parse("runtime/test.h", extra_args);
meta::Cursor cursor = parser.GetCursor();
for (auto &child : cursor.GetChildren()) {
auto kind = child.GetKind();
std::vector<std::string> cur_namespaces;
meta::Registry::Visit(cursor, cur_namespaces);
if (child.IsDefinition() && (kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl)) {
LOGV(kTag,
"class={}, file={}, spelling={}",
child.GetDisplayName(),
child.GetSourceFile(),
child.GetSpelling());
} else if (kind == CXCursor_Namespace) {
}
for (auto &clz : meta::Registry::Instance()->classes()) {
LOGI(kTag, "{}\n{}", meta::Registry::Instance()->GetQualifiedName(clz), clz->ToString());
}
return 0;

View File

@ -1,23 +0,0 @@
#pragma once
#ifndef META_CLANG_META_INFO_H
#define META_CLANG_META_INFO_H
#include "clang/cursor.h"
#include <string>
#include <unordered_map>
namespace meta {
class MetaInfo {
public:
MetaInfo(const Cursor &cursor);
std::string GetProperty(const std::string &key) const;
bool GetFlag(const std::string &key) const;
private:
using Property = std::pair<std::string, std::string>;
std::unordered_map<std::string, Property> properties_;
std::vector<Property> ExtractProperties(const Cursor &cursor) const;
};
}// namespace meta
#endif// META_CLANG_META_INFO_H

View File

@ -1,18 +0,0 @@
#pragma once
#ifndef META_CLANG_PARSER_PARSER_H
#define META_CLANG_PARSER_PARSER_H
#include <clang-c/Index.h>
#include <string>
namespace meta {
class MetaParser {
public:
MetaParser(const std::string &file_name);
~MetaParser();
private:
CXIndex index_;
CXTranslationUnit translation_unit_;
};
}// namespace meta
#endif// META_CLANG_PARSER_PARSER_H

57
src/registry.cc Normal file
View File

@ -0,0 +1,57 @@
#include "registry.h"
#include <sled/strings/utils.h>
namespace meta {
void
Registry::Visit(const meta::Cursor &cursor, std::vector<std::string> &cur_namespaces)
{
for (auto &child : cursor.GetChildren()) {
auto kind = child.GetKind();
if (child.IsDefinition() && (kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl)) {
auto clz = new meta::Class(child, cur_namespaces);
meta::Registry::Instance()->AddClass(clz);
} else if (kind == CXCursor_EnumDecl) {
} else if (kind == CXCursor_Namespace) {
std::string ns = child.GetDisplayName();
if (!ns.empty()) {
cur_namespaces.push_back(ns);
Visit(child, cur_namespaces);
cur_namespaces.pop_back();
}
}
}
}
Registry *
Registry::Instance()
{
static Registry registry;
return &registry;
}
Registry::~Registry()
{
for (auto &cls : classes_) { delete cls; }
}
void
Registry::AddClass(Class *cls)
{
if (!cls->ShouldGenerate()) { return; }
const std::string key = sled::StrJoin(cls->namespaces(), "::") + "::" + cls->name();
if (class_by_qualified_name_.find(key) == class_by_qualified_name_.end()) {
class_by_qualified_name_[key] = cls;
qualified_name_by_class_[cls] = key;
classes_.push_back(cls);
}
}
std::string
Registry::GetQualifiedName(Class *clz) const
{
auto it = qualified_name_by_class_.find(clz);
if (it != qualified_name_by_class_.end()) { return it->second; }
return "";
}
}// namespace meta

29
src/registry.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#ifndef META_REGISTRY_H
#define META_REGISTRY_H
#include "types/class.h"
#include "types/field.h"
#include "types/method.h"
#include <unordered_map>
namespace meta {
class Registry {
public:
static Registry *Instance();
static void Visit(const Cursor &cursor, std::vector<std::string> &namespaces);
~Registry();
void AddClass(Class *clz);
std::string GetQualifiedName(Class *clz) const;
std::vector<Class *> classes() const { return classes_; }
private:
Registry() = default;
// <namespace, Class>
std::unordered_map<std::string, Class *> class_by_qualified_name_;
std::unordered_map<Class *, std::string> qualified_name_by_class_;
std::vector<Class *> classes_;
};
}// namespace meta
#endif// META_REGISTRY_H

40
src/types/base_type.cc Normal file
View File

@ -0,0 +1,40 @@
#include "base_type.h"
#include <sled/strings/utils.h>
#include <sstream>
namespace meta {
BaseType::BaseType(const Cursor &cursor, const std::vector<std::string> &namespaces)
: cursor_(cursor),
namespaces_(namespaces)
{
for (auto &child : cursor.GetChildren()) {
switch (child.GetKind()) {
case CXCursor_AnnotateAttr:
properties_ = GetProperties(child);
break;
default:
break;
}
}
}
std::vector<std::string>
BaseType::GetProperties(const Cursor &annotation_cursor)
{
auto annotation = annotation_cursor.GetSpelling();
auto properties = sled::StrSplit(annotation, ",");
for (auto &property : properties) { property = sled::Trim(property, " \t\n\r\""); }
return properties;
}
std::string
BaseType::ToString() const
{
std::stringstream ss;
ss << name();
if (!properties_.empty()) { ss << " [" << sled::StrJoin(properties(), ",") << "]"; }
return ss.str();
}
}// namespace meta

39
src/types/base_type.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#ifndef META_TYPES_TYPE_INTERFACE_H
#define META_TYPES_TYPE_INTERFACE_H
#include "clang/cursor.h"
namespace meta {
class BaseType {
public:
static std::vector<std::string> GetProperties(const Cursor &annotation_cursor);
BaseType(const Cursor &cursor, const std::vector<std::string> &namespaces);
virtual ~BaseType() = default;
virtual std::string name() const { return cursor_.GetSpelling(); }
virtual std::string ToString() const;
virtual bool ShouldGenerate() const { return !properties_.empty(); }
bool HasProperty(const std::string &property) const
{
return std::find(properties_.begin(), properties_.end(), property) != properties_.end();
};
const Cursor &cursor() const { return cursor_; }
std::vector<std::string> namespaces() const { return namespaces_; }
std::vector<std::string> properties() const { return properties_; }
protected:
const Cursor cursor_;
const std::vector<std::string> namespaces_;
std::vector<std::string> properties_;
};// namespace meta
}// namespace meta
#endif// META_TYPES_TYPE_INTERFACE_H

56
src/types/class.cc Normal file
View File

@ -0,0 +1,56 @@
#include "class.h"
#include "field.h"
#include "method.h"
#include "registry.h"
#include <sled/log/log.h>
#include <sstream>
namespace meta {
Class::Class(const Cursor &cursor, const std::vector<std::string> &namespaces)
: BaseType(cursor, namespaces),
should_generate_(BaseType::ShouldGenerate())
{
for (auto &child : cursor.GetChildren()) {
switch (child.GetKind()) {
case CXCursor_Constructor:
break;
case CXCursor_CXXMethod:
methods_.push_back(new Method(child, namespaces_, *this));
should_generate_ = should_generate_ || methods_.back()->ShouldGenerate();
break;
case CXCursor_FieldDecl:
fields_.push_back(new Field(child, namespaces_, *this));
should_generate_ = should_generate_ || fields_.back()->ShouldGenerate();
break;
case CXCursor_ClassDecl:
case CXCursor_StructDecl: {
auto cur_namespaces = namespaces_;
cur_namespaces.push_back(cursor.GetSpelling());
auto clz = new Class(child, cur_namespaces);
Registry::Instance()->AddClass(clz);
break;
}
default:
break;
}
}
}
Class::~Class()
{
for (auto &field : fields_) { delete field; }
}
std::string
Class::ToString() const
{
std::stringstream ss;
ss << "class " << BaseType::ToString() << " {\n";
for (auto &field : fields_) { ss << " " << field->ToString() << ";\n"; }
ss << "\n";
for (auto &method : methods_) { ss << " " << method->ToString() << ";\n"; }
ss << "};\n";
return ss.str();
}
}// namespace meta

35
src/types/class.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#ifndef META_TYPES_CLASS_H
#define META_TYPES_CLASS_H
#include "base_type.h"
#include "clang/cursor.h"
#include <sled/strings/utils.h>
#include <string>
#include <vector>
namespace meta {
class Field;
class Method;
class Class : public BaseType {
public:
constexpr static const char *kTag = "class";
Class(const Cursor &cursor, const std::vector<std::string> &namespaces);
~Class() override;
std::string ToString() const override;
bool ShouldGenerate() const override { return should_generate_; }
const std::vector<Field *> &fields() const { return fields_; }
const std::vector<Method *> &methods() const { return methods_; }
private:
std::vector<Field *> fields_;
std::vector<Method *> methods_;
bool should_generate_;
};
}// namespace meta
#endif// META_TYPES_CLASS_H

25
src/types/field.cc Normal file
View File

@ -0,0 +1,25 @@
#include "field.h"
#include <sled/log/log.h>
namespace meta {
Field::Field(const Cursor &cursor, const std::vector<std::string> &namespaces, const Class &parent)
: BaseType(cursor, namespaces),
parent_(parent)
{
for (auto &child : cursor.GetChildren()) {
switch (child.GetKind()) {
case CXCursor_AnnotateAttr:
break;
default:
break;
}
}
}
std::string
Field::ToString() const
{
return BaseType::ToString();
}
}// namespace meta

21
src/types/field.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#ifndef META_TYPES_FIELD_H
#define META_TYPES_FIELD_H
#include "class.h"
#include "clang/cursor.h"
namespace meta {
class Field : public BaseType {
public:
constexpr static const char *kTag = "field";
Field(const Cursor &cursor, const std::vector<std::string> &namespaces, const Class &parent);
~Field() override = default;
std::string ToString() const override;
private:
const Class &parent_;
};
}// namespace meta
#endif// META_TYPES_FIELD_H

17
src/types/method.cc Normal file
View File

@ -0,0 +1,17 @@
#include "method.h"
namespace meta {
Method::Method(const Cursor &cursor,
const std::vector<std::string> &namespaces,
const Class &parent)
: BaseType(cursor, namespaces),
parent_(parent)
{}
std::string
Method::ToString() const
{
return BaseType::ToString();
}
}// namespace meta

19
src/types/method.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#ifndef META_TYPES_METHOD_H
#define META_TYPES_METHOD_H
#include "class.h"
namespace meta {
class Method : public BaseType {
public:
constexpr static const char *kTag = "method";
Method(const Cursor &cursor, const std::vector<std::string> &namespaces, const Class &parent);
~Method() override = default;
std::string ToString() const override;
private:
const Class &parent_;
};
}// namespace meta
#endif// META_TYPES_METHOD_H