Attribute & Reflection Libraries for C++

Attribute & Reflection libraries for C++?

You could have a look at the two tools below. I've never used either of them, so I can't tell you how (im)practical they are.

XRTTI:

Xrtti is a tool and accompanying C++ library which extends the standard runtime type system of C++ to provide a much richer set of reflection information about classes and methods to manipulate these classes and their members.

OpenC++:

OpenC++ is C++ frontend library (lexer+parser+DOM/MOP) and source-to-source translator. OpenC++ enables development of C++ language tools, extensions, domain specific compiler optimizations and runtime metaobject protocols.

Resources for Reflection and Attributes usage

I don't know of a good comprehensive resource. In my experience, the best way to learn is to run into a situation that can't be solved without it (at least not easily). So if you know the basic situations where reflection & attributes make things easier, you'll know when you need to start learning them.

Reflection: appropriate when you can't do what you need without it.

I know that sounds a little snarky, but it's actually true - if you need to write some code but there is just no way to figure out the types of the objects you're dealing with, reflection is appropriate. It's also appropriate when you need to sneak in and use private/protected/internal members of an object you don't control.

Attributes: appropriate when you need to add extra information about a type.

Example: I have a bunch of IConverter classes. Each one corresponds to a specific field in our content management system. How can I decorate this class with extra information which describes the field it is used for?

  • I could create a different interface for every converter, even though they all do the same thing, but that seems like a bad idea.
  • I could put a read-only property that returns a hard-coded string to the name of the field on each class, but then I'd have to create an instance of each one to check the property value.

It would be nice if I could just put some kind of extra piece of information on the class itself that says what field it's for. Then I can check through all my types and find the one I want, and just instantiate that one. Attributes let me do that:

[HandlesField("FieldName")]
public class FooFieldConverter : IConverter

Runtime Reflection Library for c++ that allow serialization and deserialization of custom types

The Oops library is doing what you are looking for. It is written for serialization using reflection.

https://bitbucket.org/barczpe/oops

You can find examples and some documentation on the wiki page.

I implemented the classes from your class diagram and added the type description or reflection to all classes.
It handles the yaml file for you. Your code does not need to deal with the yaml tree and connect the yaml nodes with the members of the classes.
This is all done by Oops. This is faster and simpler. Oops has its own yaml parser, and it does not build that immediate data-structure from the yaml file.
However, I do not recommend using yaml file format. It is not the best for serializing C++ objects.
Use the text format of the Oops library if you have a choice.
It is similar, but designed for storing C++ objects.

Here is the code (what you can also find in the AbstractClasses_test.cpp file of Oops unit test):

#include "SerializationTest.h"
#include <oops/Enum2StringTool.h>
#include <oops/rPropInterface.h>
#include <gtest/gtest.h>
#include <string>

using namespace rOops;

class IComponent
{
public:
virtual ~IComponent() = default;
IComponent() = default;
virtual void init(short) = 0;
rOOPS_ADD_PROPERTY_INTERFACE_ABSTRACT(IComponent)
{
}
};

DECLARE_ENUM_CLASS(CameraType, std::uint8_t,
eNone = 0,
eType1,
eType2
);
rOOPS_DECLARE_ENUM_TYPE_INFO( CameraType, CameraType2String )

class Camera : public IComponent {
public:
Camera() = default;
void init(short p) override
{
height = width = p;
}
private:
CameraType type{CameraType::eNone};
int height{0};
int width{0};

rOOPS_ADD_PROPERTY_INTERFACE(Camera)
{
rOOPS_INHERIT(IComponent);
rOOPS_PROPERTY(type);
rOOPS_PROPERTY(height);
rOOPS_PROPERTY(width);
}
};

rOOPS_DECLARE_STL_LIST_TYPE_INFO(std::vector<long>)

class Mesh : public IComponent
{
public:
Mesh() = default;
void init(short p) override
{
indices.push_back(p);
path = std::to_string(p);
}
private:
std::vector<long> indices;
std::string path;
rOOPS_ADD_PROPERTY_INTERFACE(Mesh)
{
rOOPS_INHERIT(IComponent);
rOOPS_PROPERTY(indices);
rOOPS_PROPERTY(path);
}
};

class ILight : public IComponent
{
public:
~ILight() override = default;
ILight() = default;
rOOPS_ADD_PROPERTY_INTERFACE_ABSTRACT(ILight)
{
rOOPS_INHERIT(IComponent);
}
};

using Vec3 = std::array<double, 3>;
rOOPS_DECLARE_STL_ARRAY_TYPE_INFO(Vec3)

class PointLight : public ILight
{
public:
PointLight() = default;
void init(short p) override
{
AO[0] = Diffuse[1] = Specular[2] = p;
}
private:
Vec3 AO{};
Vec3 Diffuse{};
Vec3 Specular{};
rOOPS_ADD_PROPERTY_INTERFACE(PointLight)
{
rOOPS_INHERIT(ILight);
rOOPS_PROPERTY(AO);
rOOPS_PROPERTY(Diffuse);
rOOPS_PROPERTY(Specular);
}
};

rOOPS_DECLARE_STL_LIST_TYPE_INFO(std::vector<std::unique_ptr<IComponent>>)

TEST(AbstractClassesTest, IComponents)
{
std::vector<std::unique_ptr<IComponent>> v;
v.push_back(std::make_unique<Camera>()); v.back()->init(1);
v.push_back(std::make_unique<Mesh>()); v.back()->init(2);
v.push_back(std::make_unique<PointLight>()); v.back()->init(3);

std::stringstream strm(cStringStreamMode);
rOopsYamlFormat format(strm);
save(format, v, "Components");
std::cout << "========== save ==========" << std::endl;
std::cout << strm.str() << std::endl;
std::cout << "==========================" << std::endl;

rOopsYamlParser parser(strm, "Components");
std::vector<std::unique_ptr<IComponent>> v2;
load(parser, v2);
}

And here is the yaml file:

Components: !std::vector<std::unique_ptr<IComponent>>
- !Camera&94841900126576
IComponent:
type: eNone
height: 1
width: 1
- !Mesh&94841900120480
IComponent:
indices:
- 2
path: "2"
- !PointLight&94841900126768
ILight:
IComponent:
AO:
- 3
- 0
- 0
Diffuse:
- 0
- 3
- 0
Specular:
- 0
- 0
- 3

How can I add reflection to a C++ application?

Ponder is a C++ reflection library, in answer to this question. I considered the options and decided to make my own since I couldn't find one that ticked all my boxes.

Although there are great answers to this question, I don't want to use tonnes of macros, or rely on Boost. Boost is a great library, but there are lots of small bespoke C++0x projects out that are simpler and have faster compile times. There are also advantages to being able to decorate a class externally, like wrapping a C++ library that doesn't (yet?) support C++11. It is fork of CAMP, using C++11, that no longer requires Boost.

Reflection - get attribute name and value on property

Use typeof(Book).GetProperties() to get an array of PropertyInfo instances. Then use GetCustomAttributes() on each PropertyInfo to see if any of them have the Author Attribute type. If they do, you can get the name of the property from the property info and the attribute values from the attribute.

Something along these lines to scan a type for properties that have a specific attribute type and to return data in a dictionary (note that this can be made more dynamic by passing types into the routine):

public static Dictionary<string, string> GetAuthors()
{
Dictionary<string, string> _dict = new Dictionary<string, string>();

PropertyInfo[] props = typeof(Book).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
AuthorAttribute authAttr = attr as AuthorAttribute;
if (authAttr != null)
{
string propName = prop.Name;
string auth = authAttr.Name;

_dict.Add(propName, auth);
}
}
}

return _dict;
}


Related Topics



Leave a reply



Submit