LINUX.ORG.RU

Генерация классов на основе декларативного описания

 , , x macros, ,


0

2

Я хочу реализовать генерацию over9000 классов/структур на основе их декларативного описания. Пример описания (формат произвольный):

Action1
    int ac1member1
    string ac1member2
    int ac1member3

Action2
    int ac2member1
    int ac2member2

Action3
   string ac3member1
   string ac3member3

На основе этого я хочу сгенерировать классы, которые содержат в себе:

  • функции инициализации (или конструкторы)
  • enum с перечнем всех member'ов
  • статический массив строк с названием member'ов
  • геттеры/сеттеры с валидацией (все входящие данные - строки, нужно проверять их корректность, чтобы программа не вывалилась при ошибке преобразования строки в число

Частично мне помогло обмазывание X Macros'ами:


#define MEMBERS \
    X(int, =0, member1) \
    X(int, =0, member2) \
    X(string, , member3)
    
enum
{
    #define X(a, b, c) E_##c,
    MEMBERS
    #undef X
    NUM_MEMBERS
};

static constexpr const char* names[NUM_MEMBERS] =
{
    #define X(a, b, c) #c,
    MEMBERS
    #undef X
};

#define X(a, b) a c b;
MEMBERS
#undef X


//==================
// сгенерирует мне няшное:

enum
{
    E_member1,
    E_member2,
    E_member3,
    NUM_MEMBERS
};

static constexpr const char* names[NUM_MEMBERS] =
{
    "member1",
    "member2",
    "member3",
};

int member1 =0;
int member2 =0;
string member3;


//подобным образом можно генерировать и геттеры/сеттеры
//так же можно и добавить ещё одно необязательное поле для валидатора

Как сгенерировать заготовки классов — тоже ясно.

Но вот как в зависимости от класса генерировать нужные внутренности?

Хотелось бы мультиразмерного X Macros, чтобы был не список, а двухмерная матрица.

P.S. Я понимаю, что было бы очень легко всё это сгенерировать на Perl. Но хотелось бы побольше задействовать нативных средств C/C++.

Но вот как в зависимости от класса генерировать нужные внутренности?

А по какому принципу генерировать эти самые внутренности, исходя из приведенного описания?

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Для класса Action1 должны быть мемберы, функции и enum'ы для ac1member1, ac1member2 и ac1member3.

Для класса Action2 должны быть мемберы, функции и enum'ы для ac2member1 и ac2member2.

И так далее.

Больше ничего не надо. Это что-то вроде типобезопасного враппера.

Chaser_Andrey ★★★★★
() автор топика
Ответ на: комментарий от yoghurt

Тело внутри функций шаблонное, никакой уникальной реализации для отдельных классов.

Все различия — в количестве и названиях мемберов.

Chaser_Andrey ★★★★★
() автор топика
Ответ на: комментарий от yoghurt

Каждый класс наследуется от одного базового, у которого есть шаблонные сеттеры/геттеры для разных типов и внутреннее хранилище.

Генерируемые сеттеры/сеттеры будут выглядеть примерно так:

inline void setFoo(const Foo& data) { setValue(names[Foo], data); }
inline int getFoo() const { value(names[Foo]); }

Chaser_Andrey ★★★★★
() автор топика

Макросами генерить код не круто. Пробуй на основе обычных текстовых шаблонов, раз структура не сложная.

ZUKMAN
()

А зачем это?

Dron ★★★★★
()
Ответ на: комментарий от Chaser_Andrey

В принципе, ничего сложного

#include <iostream>
#include <map>
#include <functional>
#include <tuple>

namespace detail {
   template <class T, std::size_t N, class... Args>
   struct get_number_of_element_from_tuple_by_type_impl {
      static constexpr auto value = N;
   };

   template <class T, std::size_t N, class... Args>
   struct get_number_of_element_from_tuple_by_type_impl<T, N, T, Args...> {
      static constexpr auto value = N;
   };
  
   template <class T, std::size_t N, class U, class... Args>
   struct get_number_of_element_from_tuple_by_type_impl<T, N, U, Args...> {
      static constexpr auto value = get_number_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
   };

}

template <class T, class... Args>
T& get_element_by_type(std::tuple<Args...>& t) {
    return std::get<detail::get_number_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t);
}

class BaseObject {
   template<class T> class MapType: public std::map<std::string, T> {};

   typedef MapType<std::string> StringMap;
   typedef MapType<int> IntMap;
   typedef MapType<bool> BoolMap;

   typedef std::tuple<StringMap, IntMap, BoolMap> MapTuple;
   MapTuple _maps;

public:
   template<typename T>
   void set(const std::string &field, const T &val) {
      typedef MapType<T> MT;
      MT &mt = get_element_by_type<MT>(_maps);
      mt[field] = val;
   }
   
   template<typename T>
   T get(const std::string &field) {
      typedef MapType<T> MT;
      MT &mt = get_element_by_type<MT>(_maps);
      return mt.at(field);
   }
};

#define defclass(I, X) \
   I##_OPEN(X)
      
#define deffield(I, T, F) \
   I##_FIELD(T, F)
   
#define endclass(I, X) \
   I##_CLOSE(X)


#define ClassWriter_OPEN(X) \ 
   class X: public BaseObject { \
   public: \
      
#define ClassWriter_FIELD(T, F) \
   protected: \
      T F; \
   public: \
      void set_##F(const T &t) { set<T>(#F, t); } \ 
      T get_##F() { return get<T>(#F); }

#define ClassWriter_CLOSE(X) \
   };


#define FieldWriter_OPEN(X) \
   enum X##_Fields {

#define FieldWriter_FIELD(T, F) \
   E_##F, \

#define FieldWriter_CLOSE(X) \
   X##_NUM_MEMBERS };


#define FieldNameWriter_OPEN(X) \
   static const char* X##_FieldNames[] = {

#define FieldNameWriter_FIELD(T, F) \
   #F,

#define FieldNameWriter_CLOSE(X) \
   };

#define build(ClassDesc) \
   ClassDesc(ClassWriter) \
   ClassDesc(FieldWriter) \
   ClassDesc(FieldNameWriter)

#define FooClass(I) \
   defclass(I, Foo) \
     deffield(I, int, foo) \
     deffield(I, std::string, bar) \
     deffield(I, bool, baz) \
   endclass(I, Foo)

build(FooClass)

int main() {
   for (int i = 0; i < Foo_NUM_MEMBERS; i++) {
      std::cout << i << ": " << Foo_FieldNames[i] << std::endl;
   }

   Foo foo;
   foo.set_foo(3);
   foo.set_bar("SI KREST KREST - SILA!");
   foo.set_baz(false);

   std::cout << "---\n"
             << foo.get_foo() << "\n"
             << foo.get_bar() << "\n"
             << foo.get_baz() << "\n"
             << std::endl;

   return 0;
}

Выхлоп:

0: foo
1: bar
2: baz
---
3
SI KREST KREST - SILA!
0

Основной затык был в том, что std::get пока не умеет принимать тип вместо индекса для доступа к элементу кортежа, обещают в C++14x.

В принципе, можно сделать красивее, чтобы enum полей и их имена были членами определяемого класса (Foo::*), но мне лень.

yoghurt ★★★★★
()
Ответ на: В принципе, ничего сложного от yoghurt

(между делом)

Интересно, если потенциальный работодатель увидит такой перл от соискателя, в какую сторону могло бы повлиять на решение о приёме? :)

yoghurt ★★★★★
()

У меня такое генерится на баше. Речь идет о протоколе DIAMETER. Описаны команды и AVP в формате похожем на приведенный в RFC. Генерятся всякие словари, парсер, классы, сериализаторы, дока на HTML итд.. из 236 кб описаний получается 7 мб кода.

vromanov ★★★
()

так и до хаскеля дойдёшь

Bad_ptr ★★★★★
()

Почему бы сразу не написать на нормальном языке?

anonymous
()

пост не читал, генери из UML в <any_language>.

x0r ★★★★★
()

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Type name="Action">
	<Field name="Hash" label="Hash" Type="TEXT"/>
	<Field name="Standard" label="Is standard paper" Type="BOOL"/>
	<Field name="Lx" label="Lx" Type="REAL"/>
	<Field name="Mass" label="Mass" Type="INTEGER"/>
	<PrimaryKey name="Hash"/>
</Type>

XSLT для генерации заголовочного файла:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="text" indent="no" encoding="UTF-8"/>
	<xsl:template match="/">
		<xsl:apply-templates select="Type"/>
	</xsl:template>
	<xsl:template match="Type">
		<xsl:text>#ifndef __GEN_TYPE_</xsl:text><xsl:value-of select="@name"/><xsl:text>_H__&#xa;</xsl:text>
		<xsl:text>#define __GEN_TYPE_</xsl:text><xsl:value-of select="@name"/><xsl:text>_H__&#xa;</xsl:text>
		<xsl:text>// This file generated!!!&#xa;</xsl:text>
		<xsl:text>#include "QObject"&#xa;</xsl:text>

		<xsl:text>class </xsl:text><xsl:value-of select="@name"/><xsl:text> : public QObject&#xa;</xsl:text>
		<xsl:text>{&#xa;</xsl:text>
		<xsl:text>	Q_OBJECT&#xa;</xsl:text>
		<xsl:apply-templates select="Field" mode="Q_PROPERTY"/>
		<xsl:text>private:&#xa;</xsl:text>
		<xsl:apply-templates select="Field" mode="Private"/>
		<xsl:text>public:&#xa;</xsl:text>
		<xsl:text>	</xsl:text><xsl:value-of select="@name"/><xsl:text>(QObject* obj = NULL);&#xa;</xsl:text>
		<xsl:apply-templates select="Field" mode="Method"/>
		<xsl:text>}&#xa;</xsl:text>
		<xsl:text>#endif // __GEN_TYPE_</xsl:text><xsl:value-of select="@name"/><xsl:text>_H__&#xa;</xsl:text>
	</xsl:template>
	<xsl:template match="Field" mode="Q_PROPERTY">
		<xsl:text>&#9;Q_PROPERTY(</xsl:text>
		<xsl:apply-templates select="@Type"/>
		<xsl:text> </xsl:text><xsl:value-of select="@name"/>
		<xsl:text> READ get</xsl:text><xsl:value-of select="@name"/>
		<xsl:text> WRITE set</xsl:text><xsl:value-of select="@name"/>
		<xsl:text>)&#xa;</xsl:text>
	</xsl:template>
	<xsl:template match="Field" mode="Private">
		<xsl:text>&#9;</xsl:text>
		<xsl:apply-templates select="@Type"/>
		<xsl:text> m</xsl:text><xsl:value-of select="@name"/><xsl:text>;&#xa;</xsl:text>
	</xsl:template>
	<xsl:template match="Field" mode="Method">
		<xsl:text>&#xa;&#9;</xsl:text>
		<xsl:apply-templates select="@Type"/>
		<xsl:text> get</xsl:text><xsl:value-of select="@name"/><xsl:text>() const;&#xa;</xsl:text>
		<xsl:text>&#9;void set</xsl:text><xsl:value-of select="@name"/><xsl:text>(</xsl:text>
		<xsl:apply-templates select="@Type"/>
		<xsl:text> value);&#xa;</xsl:text>
	</xsl:template>
	<xsl:template match="@Type">
		<xsl:if test=".='REAL'"><xsl:text>double</xsl:text></xsl:if>
		<xsl:if test=".='BOOL'"><xsl:text>bool</xsl:text></xsl:if>
		<xsl:if test=".='INTEGER'"><xsl:text>int</xsl:text></xsl:if>
		<xsl:if test=".='TEXT'"><xsl:text>QString</xsl:text></xsl:if>
	</xsl:template>
</xsl:stylesheet>

Результат:

#ifndef __GEN_TYPE_Action_H__
#define __GEN_TYPE_Action_H__
// This file generated!!!
#include "QObject"
class Action : public QObject
{
	Q_OBJECT
	Q_PROPERTY(QString Hash READ getHash WRITE setHash)
	Q_PROPERTY(bool Standard READ getStandard WRITE setStandard)
	Q_PROPERTY(double Lx READ getLx WRITE setLx)
	Q_PROPERTY(int Mass READ getMass WRITE setMass)
private:
	QString mHash;
	bool mStandard;
	double mLx;
	int mMass;
public:
	Action(QObject* obj = NULL);

	QString getHash() const;
	void setHash(QString value);

	bool getStandard() const;
	void setStandard(bool value);

	double getLx() const;
	void setLx(double value);

	int getMass() const;
	void setMass(int value);
}
#endif // __GEN_TYPE_Action_H__

Аналогично cpp-файл, SQL-запросы и прочую лабуду, если надо.

AlexVR ★★★★★
()

Ну и всё это в систему сборки, для автоматизации.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

ЛОЛ. Сопротивление бесполезно, программирование всегда будет в жопе.

anonymous
()
Ответ на: комментарий от Chaser_Andrey

XSLT эта та еще фигня. Но если как у ТС 9000 классов. То уже и ничего. Тем более что XSLT один для всех классов. А одно описание класса для всех реализаций.

AlexVR ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.