transformer
transformer copied to clipboard
Tool for parsing C/C++ code and generating output using a given Jinja-like template
Transformer - C/C++ code parser and generator
Tool for parsing C/C++ code and generating output using a given Jinja-like template. This might be useful for creating reflection/RTTI frameworks for C/C++ (or you can just wait until 2023 or 2026 year and use standard implementation... ~~or use Java~~)
Currently supported features:
- struct/class fields parsing
- struct/class methods (including constructors) parsing
- enum parsing
- generate output using Jinja-like template
- C++ custom attributes support
WARNING! WIP!
After switching from libclang
to libtooling
, some of the information in this README
file is OUTDATED. At the moment transformer
is a fully working tool, but some command line interfaces have changed a little, and also names of some template variables have changed. I will try to update this manual in the near future.
Usage example
Let's imagine that we have some files:
example/simple/source.hpp
#include <vector>
namespace math::geometry
{
struct Point2D
{
float x;
float y;
};
struct Point3D : public Point2D
{
float z;
};
enum class Direction : long
{
UP = 0,
DOWN,
LEFT = -10,
RIGHT
};
class Shape
{
private:
std::vector<Point3D> m_points;
};
}
example/simple/template.jinja
We have in file {{ file_path }} {{ length(classes) }} classes:
{% for c in classes %}
{% if length(c.base_classes) != 0 %}
{{ c.name }} has {{ length(c.base_classes) }} base class:
{% endif %}
{% for b in c.base_classes %}
- {{ b.name }} from file {{ b.file_path }}
{% endfor %}
{% if length(c.base_classes) == 0 %}
{{ c.name }} does not have base classes :(
{% endif %}
{% if length(c.fields) != 0 %}
{{ c.name }} has {{ length(c.fields) }} fields:
{% endif %}
{% for f in c.fields %}
- {{ f.type }} -> {{ f.name }} (full name is {{ f.full_name }})
{% endfor %}
{% if loop.is_last %}
THE END!
{% endif %}
{% endfor %}
{% for e in enums %}
Enum {{ e.name }} has type {{ e.integer_type }}:
{% for c in e.consts %}
- {{ c.full_name }} has value {{ c.value }}
{% endfor %}
{% endfor %}
After execution of command transformer example/simple/template.jinja example/simple/source.hpp
the output will be:
We have in file example/simple/source.hpp 3 classes:
Point2D does not have base classes :(
Point2D has 2 fields:
- float -> x (full name is math::geometry::Point2D::x)
- float -> y (full name is math::geometry::Point2D::y)
Point3D has 1 base class:
- Point2D from file example/simple/source.hpp
Point3D has 3 fields:
- float -> x (full name is math::geometry::Point2D::x)
- float -> y (full name is math::geometry::Point2D::y)
- float -> z (full name is math::geometry::Point3D::z)
Shape does not have base classes :(
Shape has 1 fields:
- std::vector<Point3D> -> m_points (full name is math::geometry::Shape::m_points)
THE END!
Enum Direction has type long:
- math::geometry::Direction::UP has value 0
- math::geometry::Direction::DOWN has value 1
- math::geometry::Direction::LEFT has value -10
- math::geometry::Direction::RIGHT has value -9
For more information about tool usage execute command:
transformer --help
Beta! RTTI framework for C++
You can also find a more advanced example of creating a RTTI framework for C++ in the directory example/rtti/
. Some examples with it:
#include "rtti/ObjectType.h"
struct Point2D
{
float x;
float y;
};
struct Point3D : Point2D
{
float z;
};
// all we need is to inherit from rtti::ObjectType class and use RTTI_OBJECT_TYPE_BODY macro
class Rect : public rtti::ObjectType<Rect>
{
private: // <- we even have access to private fields
RTTI_OBJECT_TYPE_BODY;
std::string name;
Point3D top_left;
Point3D bot_right;
};
int main()
{
Rect r {};
r.object_field("name") = "Hello, Rect!";
r.object_field("top_left") = 123; // <- wrong type
r.object_field("bot_right") = { { "x", 12345.f } };
r.object_field("bot_right").subfield("x") = -1.5f;
r.object_set_value(
{
{ "top_left", {
{ "x", 123u },
{ "z", 3.0 },
{ "y", 5.1f },
} },
{ "name", "New name from " __FILE__ },
{ "some name", 123 - 456 }
}
);
}
Requirements
Third-party libs
- inja for parsing and rendering Jinja-like templates
-
json required by
inja
for data input and handling
Downloading and compilation
This is a piece of cake:
git clone --recursive https://github.com/mtiapko/transformer
make
Compiled tool binary will be in bin/
directory.
But!
Be careful, nlohmann/json
is very heavy repo (240MB+). But inja
requires only source files (only include directory). So you can clone this repo without --recursive
if nlohmann/json
already exists in your filesystem or manually download only source files and put them in libs/json/include/nlohmann/<header-files>
.
Troubleshooting
[ clang ] fatal error: 'stddef.h' file not found
This error occurs when clang
looks for headers in gcc
directories. You can create symlink, but transformer
support arguments for compiler, so you can set system header directory path:
transformer <template-file-path> <source-file-path> --compiler-args -cxx-isystem /usr/lib/clang/<clang-version>/include