Add cpp part of a bindings generator

master
Jos van den Oever 2017-08-06 15:57:51 +02:00
parent 5a52613025
commit 3124cdb1ae
5 changed files with 421 additions and 5 deletions

View File

@ -33,7 +33,6 @@ ExternalProject_Add(
INSTALL_COMMAND ""
LOG_BUILD ON)
# Find Qt modules
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core # QCommandLineParser, QStringLiteral
@ -48,8 +47,11 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
add_subdirectory(rust_qt_binding_generator)
set(RMail_SRCS src/main.cpp src/RMailObject.cpp)
set(RMail_SRCS src/main.cpp src/RMailObject.cpp src/tmp.cpp)
add_executable(RMail ${RMail_SRCS})

22
bindings.json Normal file
View File

@ -0,0 +1,22 @@
{
"cppfile": "src/tmp.cpp",
"objects": [{
"name": "Test",
"properties": [{
"name": "userName",
"type": "QString",
"write": true
}, {
"name": "age",
"type": "int"
}, {
"name": "active",
"type": "bool",
"write": true
}, {
"name": "misc",
"type": "QVariant",
"write": true
}]
}]
}

View File

@ -0,0 +1,5 @@
add_executable(rust_qt_binding_generator rust_qt_binding_generator.cpp)
target_link_libraries(rust_qt_binding_generator
Qt5::Core
)

View File

@ -0,0 +1,387 @@
#include <QCommandLineParser>
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
using std::endl;
QTextStream out(stdout);
QTextStream err(stderr);
struct Property {
QString name;
QString type;
bool write;
};
struct Object {
QString name;
QList<Property> properties;
};
struct Configuration {
QFileInfo hfile;
QFileInfo cppfile;
QList<Object> objects;
};
Property
parseProperty(const QJsonObject& json) {
Property p;
p.name = json.value("name").toString();
p.type = json.value("type").toString();
p.write = json.value("write").toBool();
return p;
}
Object
parseObject(const QJsonObject& json) {
Object o;
o.name = json.value("name").toString();
for (const QJsonValue& val: json.value("properties").toArray()) {
o.properties.append(parseProperty(val.toObject()));
}
return o;
}
Configuration
parseConfiguration(const QString& path) {
QFile configurationFile(path);
const QDir base = QFileInfo(configurationFile).dir();
if (!configurationFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
err << QCoreApplication::translate("main",
"Cannot read %1.\n").arg(configurationFile.fileName());
err.flush();
exit(1);
}
const QByteArray data(configurationFile.readAll());
QJsonParseError error;
const QJsonDocument doc(QJsonDocument::fromJson(data, &error));
if (error.error != QJsonParseError::NoError) {
err << error.errorString();
err.flush();
exit(1);
}
const QJsonObject o = doc.object();
Configuration c;
c.cppfile = QFileInfo(base, o.value("cppfile").toString());
c.hfile = QFileInfo(c.cppfile.dir(), c.cppfile.completeBaseName() + ".h");
for (const QJsonValue& val: o.value("objects").toArray()) {
c.objects.append(parseObject(val.toObject()));
}
return c;
/*
QTextStream in(&configurationFile);
in.setCodec("UTF-8");
configuration = in.readAll();
configurationFile.close();
} else {
return 1;
}
*/
}
QString upperInitial(const QString& name) {
return name.left(1).toUpper() + name.mid(1);
}
QString writeProperty(const QString& name) {
return "WRITE set" + upperInitial(name) + " ";
}
QString type(const Property& p) {
if (p.type.startsWith("Q")) {
return "const " + p.type + "&";
}
return p.type;
}
QString cSetType(const Property& p) {
if (p.type.startsWith("Q")) {
return p.type.toLower() + "_t";
}
return p.type;
}
QString cGetType(const Property& p) {
return p.type + "*, " + p.type.toLower() + "_set";
}
void writeHeaderObject(QTextStream& h, const Object& o) {
h << QString(R"(
class %1Interface;
class %1 : public QObject
{
Q_OBJEC%2
%1Interface * const d;
)").arg(o.name, "T");
for (auto p: o.properties) {
h << QString(" Q_PROPERTY(%1 %2 READ %2 %3NOTIFY %2Changed FINAL)")
.arg(p.type, p.name,
p.write ? writeProperty(p.name) :"") << endl;
}
h << QString(R"(public:
explicit %1(QObject *parent = nullptr);
~%1();
)").arg(o.name);
for (auto p: o.properties) {
h << " " << p.type << " " << p.name << "() const;" << endl;
if (p.write) {
h << " void set" << upperInitial(p.name) << "(" << type(p) << " v);" << endl;
}
}
h << "signals:" << endl;
for (auto p: o.properties) {
h << " void " << p.name << "Changed();" << endl;
}
h << "private:" << endl;
for (auto p: o.properties) {
h << " " << p.type << " m_" << p.name << ";" << endl;
}
h << "};" << endl;
}
void writeObjectCDecl(QTextStream& cpp, const Object& o) {
const QString lcname(o.name.toLower());
cpp << QString(" %1Interface* %2_new(%1*").arg(o.name, lcname);
for (const Property& p: o.properties) {
cpp << QString(", void (*)(%1*)").arg(o.name);
}
cpp << ");" << endl;
cpp << QString(" void %2_free(%1Interface*);").arg(o.name, lcname)
<< endl;
for (const Property& p: o.properties) {
const QString base = QString("%1_%2").arg(lcname, p.name.toLower());
if (p.type.startsWith("Q")) {
cpp << QString(" void %2_get(%1Interface*, %3);")
.arg(o.name, base, cGetType(p)) << endl;
} else {
cpp << QString(" %3 %2_get(%1Interface*);")
.arg(o.name, base, p.type) << endl;
}
if (p.write) {
cpp << QString(" void %1_set(void*, %2);")
.arg(base, cSetType(p)) << endl;
}
}
}
void writeCppObject(QTextStream& cpp, const Object& o) {
const QString lcname(o.name.toLower());
cpp << QString("%1::%1(QObject *parent):\n QObject(parent),")
.arg(o.name) << endl;
cpp << QString(" d(%1_new(this").arg(lcname);
for (const Property& p: o.properties) {
cpp << QString(",\n [](%1* o) { emit o->%2Changed(); }")
.arg(o.name, p.name);
}
cpp << QString(R"()) {}
%1::~%1() {
%2_free(d);
}
)").arg(o.name, lcname);
for (const Property& p: o.properties) {
const QString base = QString("%1_%2").arg(lcname, p.name.toLower());
cpp << QString("%3 %1::%2() const\n{\n").arg(o.name, p.name, p.type);
if (p.type.startsWith("Q")) {
cpp << " " << p.type << " v;\n";
cpp << " " << base << "_get(d, &v, set_" << p.type.toLower()
<< ");\n";
cpp << " return v;\n}\n";
} else {
cpp << QString(" return %1_get(d);\n}\n").arg(base);
}
if (p.write) {
cpp << "void " << o.name << "::set" << upperInitial(p.name) << "(" << type(p) << " v) {" << endl;
if (p.type == "QVariant") {
cpp << QString(" variant(v, d, %1_set);").arg(base) << endl;
} else {
cpp << QString(" %1_set(d, v);").arg(base) << endl;
}
cpp << "}" << endl;
}
}
}
void writeHeader(const Configuration& conf) {
QFile hFile(conf.hfile.absoluteFilePath());
if (!hFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
err << QCoreApplication::translate("main",
"Cannot write %1.\n").arg(hFile.fileName());
err.flush();
exit(1);
}
QTextStream h(&hFile);
const QString guard(conf.hfile.fileName().replace('.', '_').toUpper());
h << QString(R"(
#ifndef %1
#define %1
#include <QObject>
#include <QString>
#include <QVariantMap>
#include <QAbstractItemModel>
)").arg(guard);
for (auto object: conf.objects) {
writeHeaderObject(h, object);
}
h << QString("#endif // %1\n").arg(guard);
}
void writeCpp(const Configuration& conf) {
QFile cppFile(conf.cppfile.absoluteFilePath());
if (!cppFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
err << QCoreApplication::translate("main",
"Cannot write %1.\n").arg(cppFile.fileName());
err.flush();
exit(1);
}
QTextStream cpp(&cppFile);
cpp << QString(R"(
#include "%1"
namespace {
struct qbytearray_t {
private:
const char* data;
int len;
public:
operator QByteArray() const {
return QByteArray(data, len);
}
};
struct qstring_t {
private:
const void* data;
int len;
public:
qstring_t(const QString& v):
data(static_cast<const void*>(v.utf16())),
len(v.size()) {
}
operator QString() const {
return QString::fromUtf8(static_cast<const char*>(data), len);
}
};
struct qmodelindex_t {
int row;
int column;
uint64_t id;
qmodelindex_t(const QModelIndex& m):
row(m.row()), column(m.column()), id(m.internalId()) {}
};
struct qvariant_t {
unsigned int type;
int value;
const char* data;
};
QVariant variant(const qvariant_t& v) {
switch (v.type) {
case QVariant::Bool: return QVariant((bool)v.value);
case QVariant::String: return QString::fromUtf8(static_cast<const char*>(v.data), v.value);
default:;
}
return QVariant();
}
void variant(const QByteArray& v, void* d, void (*set)(void*, qvariant_t)) {
set(d, {
.type = QVariant::ByteArray,
.value = v.length(),
.data = v.data()
});
}
void variant(const QString& v, void* d, void (*set)(void*, qvariant_t)) {
set(d, {
.type = QVariant::String,
.value = v.size(),
.data = static_cast<const char*>(static_cast<const void*>(v.utf16()))
});
}
void variant(const QVariant& v, void* d, void (*set)(void*, qvariant_t)) {
switch (v.type()) {
case QVariant::Bool:
set(d, {
.type = QVariant::Bool,
.value = v.toBool(),
.data = 0
});
break;
case QVariant::Int:
set(d, {
.type = QVariant::Int,
.value = v.toInt(),
.data = 0
});
break;
case QVariant::ByteArray:
variant(v.toByteArray(), d, set);
break;
case QVariant::String:
variant(v.toString(), d, set);
break;
default:
set(d, {
.type = QVariant::Invalid,
.value = 0,
.data = 0
});
}
}
}
typedef void (*qstring_set)(QString*, qstring_t*);
void set_qstring(QString* v, qstring_t* val) {
*v = *val;
}
typedef void (*qvariant_set)(QVariant*, qvariant_t*);
void set_qvariant(QVariant* v, qvariant_t* val) {
*v = variant(*val);
}
extern "C" {
)").arg(conf.hfile.fileName());
for (auto object: conf.objects) {
writeObjectCDecl(cpp, object);
}
cpp << "};" << endl;
for (auto object: conf.objects) {
writeCppObject(cpp, object);
}
}
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName(argv[0]);
QCoreApplication::setApplicationVersion("0.1");
QCommandLineParser parser;
parser.setApplicationDescription("Generates bindings between Qt and Rust");
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("configuration",
QCoreApplication::translate("main", "Configuration file"));
parser.process(app);
const QStringList args = parser.positionalArguments();
if (args.isEmpty()) {
err << QCoreApplication::translate("main",
"Configuration file is missing.\n");
return 1;
}
const QString configurationFile(args.at(0));
const Configuration configuration = parseConfiguration(configurationFile);
writeHeader(configuration);
writeCpp(configuration);
return 0;
}

View File

@ -64,18 +64,18 @@ namespace {
typedef void (*qvariant_set)(void*, qvariant_t*);
extern "C" {
RMailObjectInterface* hello_new(void*, void (*)(RMailObject*));
RMailObjectInterface* hello_new(RMailObject*, void (*)(RMailObject*));
void hello_free(RMailObjectInterface*);
void hello_set(RMailObjectInterface*, const uint16_t *, size_t);
qstring_t hello_get(RMailObjectInterface*);
RItemModelInterface* ritemmodel_new(void*, void (*)(RItemModel*));
RItemModelInterface* ritemmodel_new(RItemModel*, void (*)(RItemModel*));
void ritemmodel_free(RItemModelInterface*);
int ritemmodel_column_count(RItemModelInterface*, qmodelindex_t parent);
int ritemmodel_row_count(RItemModelInterface*, qmodelindex_t parent);
qmodelindex_t ritemmodel_index(RItemModelInterface*, int row, int column, qmodelindex_t parent);
qmodelindex_t ritemmodel_parent(RItemModelInterface*, qmodelindex_t);
void ritemmodel_data(RItemModelInterface*, qmodelindex_t, int, void*, qvariant_set);
void ritemmodel_data(RItemModelInterface*, qmodelindex_t, int, QVariant*, qvariant_set);
}
RMailObject::RMailObject(QObject *parent):