Ability to create proto specs from structures

This commit is contained in:
Matthias Koefferlein 2024-08-11 23:13:26 +02:00
parent b7cc3b87df
commit 971ef3ff72
3 changed files with 479 additions and 106 deletions

View File

@ -21,12 +21,234 @@
*/
#include "tlProtocolBufferStruct.h"
#include <cctype>
namespace tl
{
// --------------------------------------------------------------------
static size_t s_oid = 0;
// --------------------------------------------------------------------
// PBElementList implementation
PBElementList::PBElementList ()
: m_oid (++s_oid)
{
// .. nothing yet ..
}
PBElementList::PBElementList (const PBElementBase &e)
: m_oid (++s_oid)
{
m_elements.push_back (PBElementProxy (e));
}
PBElementList::PBElementList (PBElementBase *e)
: m_oid (++s_oid)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
PBElementList::PBElementList (const std::string &name, const PBElementList &d)
: m_elements (d.m_elements), m_oid (d.m_oid), m_name (name)
{
// .. nothing yet ..
}
PBElementList::PBElementList (const PBElementList &d)
: m_elements (d.m_elements), m_oid (d.m_oid), m_name (d.m_name)
{
// .. nothing yet ..
}
PBElementList::PBElementList (const PBElementList &d, const PBElementBase &e)
: m_elements (d.m_elements), m_oid (++s_oid), m_name (d.m_name)
{
m_elements.push_back (PBElementProxy (e));
}
PBElementList::PBElementList (const PBElementList &d, PBElementBase *e)
: m_elements (d.m_elements), m_oid (++s_oid), m_name (d.m_name)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
void PBElementList::append (const PBElementBase &e)
{
m_elements.push_back (PBElementProxy (e));
}
void PBElementList::append (PBElementBase *e)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
PBElementList::iterator PBElementList::begin () const
{
return m_elements.begin ();
}
PBElementList::iterator PBElementList::end () const
{
return m_elements.end ();
}
PBElementList PBElementList::empty ()
{
return PBElementList ();
}
// --------------------------------------------------------------------
// PBElementBase implementation
PBElementBase::PBElementBase (const std::string &name, int tag, const PBElementList &children)
: m_name (name), m_tag (tag), mp_children (new PBElementList (children)), m_owns_child_list (true)
{
// .. nothing yet ..
}
PBElementBase::PBElementBase (const std::string &name, int tag, const PBElementList *children)
: m_name (name), m_tag (tag), mp_children (children), m_owns_child_list (false)
{
// .. nothing yet ..
}
PBElementBase::PBElementBase (const PBElementBase &d)
: m_name (d.m_name), m_tag (d.m_tag), m_owns_child_list (d.m_owns_child_list)
{
if (m_owns_child_list) {
mp_children = new PBElementList (*d.mp_children);
} else {
mp_children = d.mp_children;
}
}
PBElementBase::~PBElementBase ()
{
if (m_owns_child_list) {
delete const_cast <PBElementList *> (mp_children);
mp_children = 0;
}
}
PBElementBase::Cardinality
PBElementBase::cardinality () const
{
return Zero;
}
PBElementBase::iterator
PBElementBase::begin () const
{
return mp_children->begin ();
}
PBElementBase::iterator
PBElementBase::end () const
{
return mp_children->end ();
}
std::string
PBElementBase::name4code () const
{
std::string res;
const char *n = name ().c_str ();
if (! isalpha (*n) && *n != '_') {
res += '_';
}
while (*n) {
if (*n == '-') {
res += '_';
} else if (isalnum (*n) || *n == '_') {
res += *n;
}
++n;
}
return res;
}
std::string
PBElementBase::create_def (std::map<size_t, std::pair<const PBElementBase *, std::string> > &messages) const
{
std::string res;
auto m = messages.find (oid ());
if (m != messages.end ()) {
res += "message " + m->second.second + " {\n";
for (auto i = begin (); i != end (); ++i) {
const PBElementBase *e = i->get ();
Cardinality c = e->cardinality ();
std::string entry = e->create_def_entry (messages);
if (! entry.empty ()) {
res += " ";
if (c != Zero) {
if (c == Many) {
res += "repeated ";
} else {
res += "optional ";
}
res += entry + "\n";
}
}
}
res += "}";
}
return res;
}
void
PBElementBase::collect_messages (std::map<size_t, std::pair<const PBElementBase *, std::string> > &messages) const
{
for (auto i = begin (); i != end (); ++i) {
i->get ()->collect_messages (messages);
}
}
std::string
PBElementBase::make_message_name () const
{
std::string res = mp_children->name ();
if (! res.empty ()) {
return res;
}
// Capitalize names
std::string n4c = name4code ();
bool upcase = true;
const char *n = n4c.c_str ();
while (*n) {
if (*n == '_') {
upcase = true;
} else if (upcase) {
res += toupper (*n);
upcase = false;
} else {
res += *n;
}
++n;
}
return res;
}
// --------------------------------------------------------------------
// PBParser implementation
PBParser::PBParser ()

View File

@ -26,6 +26,8 @@
#include "tlCommon.h"
#include "tlProtocolBuffer.h"
#include <map>
namespace tl
{
@ -277,12 +279,24 @@ private:
class TL_PUBLIC PBElementBase;
struct pass_by_value_tag {
pass_by_value_tag () { }
struct pb_pass_by_value_tag {
pb_pass_by_value_tag () { }
};
struct pass_by_ref_tag {
pass_by_ref_tag () { }
struct pb_pass_by_ref_tag {
pb_pass_by_ref_tag () { }
};
struct pb_zero_cardinality_tag {
pb_zero_cardinality_tag () { }
};
struct pb_single_cardinality_tag {
pb_single_cardinality_tag () { }
};
struct pb_many_cardinality_tag {
pb_many_cardinality_tag () { }
};
/**
@ -360,66 +374,36 @@ public:
typedef std::list <PBElementProxy> children_list;
typedef children_list::const_iterator iterator;
PBElementList ()
PBElementList ();
PBElementList (const PBElementBase &e);
PBElementList (PBElementBase *e);
PBElementList (const std::string &name, const PBElementList &d);
PBElementList (const PBElementList &d);
PBElementList (const PBElementList &d, const PBElementBase &e);
PBElementList (const PBElementList &d, PBElementBase *e);
void append (const PBElementBase &e);
void append (PBElementBase *e);
iterator begin () const;
iterator end () const;
static PBElementList empty ();
size_t oid () const
{
// .. nothing yet ..
return m_oid;
}
PBElementList (const PBElementBase &e)
const std::string &name () const
{
m_elements.push_back (PBElementProxy (e));
}
PBElementList (PBElementBase *e)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
PBElementList (const PBElementList &d, const PBElementBase &e)
: m_elements (d.m_elements)
{
m_elements.push_back (PBElementProxy (e));
}
PBElementList (const PBElementList &d, PBElementBase *e)
: m_elements (d.m_elements)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
void append (const PBElementBase &e)
{
m_elements.push_back (PBElementProxy (e));
}
void append (PBElementBase *e)
{
if (e) {
m_elements.push_back (PBElementProxy (e));
}
}
iterator begin () const
{
return m_elements.begin ();
}
iterator end () const
{
return m_elements.end ();
}
static PBElementList empty ()
{
return PBElementList ();
return m_name;
}
private:
std::list <PBElementProxy> m_elements;
size_t m_oid;
std::string m_name;
};
/**
@ -486,35 +470,14 @@ class TL_PUBLIC PBElementBase
public:
typedef PBElementList::iterator iterator;
PBElementBase (const std::string &name, int tag, const PBElementList &children)
: m_name (name), m_tag (tag), mp_children (new PBElementList (children)), m_owns_child_list (true)
{
// .. nothing yet ..
}
enum Cardinality {
Zero, Single, Many
};
PBElementBase (const std::string &name, int tag, const PBElementList *children)
: m_name (name), m_tag (tag), mp_children (children), m_owns_child_list (false)
{
// .. nothing yet ..
}
PBElementBase (const PBElementBase &d)
: m_name (d.m_name), m_tag (d.m_tag), m_owns_child_list (d.m_owns_child_list)
{
if (m_owns_child_list) {
mp_children = new PBElementList (*d.mp_children);
} else {
mp_children = d.mp_children;
}
}
virtual ~PBElementBase ()
{
if (m_owns_child_list) {
delete const_cast <PBElementList *> (mp_children);
mp_children = 0;
}
}
PBElementBase (const std::string &name, int tag, const PBElementList &children);
PBElementBase (const std::string &name, int tag, const PBElementList *children);
PBElementBase (const PBElementBase &d);
virtual ~PBElementBase ();
virtual PBElementBase *clone () const = 0;
@ -524,6 +487,13 @@ public:
virtual void write (const PBElementBase *, tl::ProtocolBufferWriterBase &, PBWriterState &) const { }
virtual Cardinality cardinality () const;
size_t oid () const
{
return mp_children->oid ();
}
int tag () const
{
return m_tag;
@ -534,14 +504,36 @@ public:
return m_name;
}
iterator begin () const
/**
* @brief Returns a name suitable for code
* Specifically, hyphens are replaced by underscores.
*/
std::string name4code () const;
iterator begin () const;
iterator end () const;
std::string create_def (std::map<size_t, std::pair <const PBElementBase *, std::string> > &messages) const;
protected:
virtual void collect_messages (std::map<size_t, std::pair <const PBElementBase *, std::string> > &messages) const;
virtual std::string create_def_entry (std::map<size_t, std::pair <const PBElementBase *, std::string> > &messages) const = 0;
std::string make_message_name () const;
static Cardinality get_cardinality (tl::pb_zero_cardinality_tag)
{
return mp_children->begin ();
return Zero;
}
iterator end () const
static Cardinality get_cardinality (tl::pb_single_cardinality_tag)
{
return mp_children->end ();
return Single;
}
static Cardinality get_cardinality (tl::pb_many_cardinality_tag)
{
return Many;
}
private:
@ -625,12 +617,35 @@ public:
r.start (*objs.back (parent_tag));
while (! r.at_end ()) {
typedef typename Read::tag read_tag_type;
read_tag_type read_tag;
write_obj (r (), tag (), writer, read_tag, objs);
write_obj (r (), tag (), writer, read_tag_type (), objs);
r.next ();
}
}
virtual Cardinality cardinality () const
{
typedef typename Read::cardinality cardinality_type;
return get_cardinality (cardinality_type ());
}
virtual void collect_messages (std::map<size_t, std::pair <const PBElementBase *, std::string> > &messages) const
{
if (messages.find (oid ()) == messages.end ()) {
messages [oid ()] = std::make_pair (this, make_message_name ());
PBElementBase::collect_messages (messages);
}
}
virtual std::string create_def_entry (std::map<size_t, std::pair <const PBElementBase *, std::string> > &messages) const
{
auto m = messages.find (oid ());
if (m != messages.end ()) {
return m->second.second + " " + name4code () + " = " + tl::to_string (tag ()) + ";";
} else {
return std::string ();
}
}
protected:
Read &rear () { return m_r; }
Write &write () { return m_w; }
@ -640,7 +655,7 @@ private:
Write m_w;
// this write helper is used if the reader delivers an object by value
void write_obj (Obj obj, int tag, tl::ProtocolBufferWriterBase &writer, tl::pass_by_value_tag, PBWriterState &objs) const
void write_obj (Obj obj, int tag, tl::ProtocolBufferWriterBase &writer, tl::pb_pass_by_value_tag, PBWriterState &objs) const
{
PBObjTag<Obj> self_tag;
@ -658,7 +673,7 @@ private:
}
}
void write_obj (const Obj &obj, int tag, tl::ProtocolBufferWriterBase &writer, tl::pass_by_ref_tag, PBWriterState &objs) const
void write_obj (const Obj &obj, int tag, tl::ProtocolBufferWriterBase &writer, tl::pb_pass_by_ref_tag, PBWriterState &objs) const
{
PBObjTag<Obj> self_tag;
@ -800,6 +815,23 @@ public:
}
}
virtual Cardinality cardinality () const
{
typedef typename Read::cardinality cardinality_type;
return get_cardinality (cardinality_type ());
}
virtual void collect_messages (std::map<size_t, std::pair <const PBElementBase *, std::string> > & /*messages*/) const
{
// no messages here.
}
virtual std::string create_def_entry (std::map<size_t, std::pair <const PBElementBase *, std::string> > & /*messages*/) const
{
const Value *v = 0;
return typestring (v) + " " + name4code () + " = " + tl::to_string (tag ()) + ";";
}
private:
Read m_r;
Write m_w;
@ -952,6 +984,74 @@ private:
reader.read (vv);
m_c.pb_decode (vv, v);
}
// type strings
std::string typestring (const float *) const
{
return "float";
}
std::string typestring (const double *) const
{
return "double";
}
std::string typestring (const uint8_t *) const
{
return "uint32";
}
std::string typestring (const uint16_t *) const
{
return "uint32";
}
std::string typestring (const uint32_t *) const
{
return "uint32";
}
std::string typestring (const uint64_t *) const
{
return "uint64";
}
std::string typestring (const int8_t *) const
{
return "sint32";
}
std::string typestring (const int16_t *) const
{
return "sint32";
}
std::string typestring (const int32_t *) const
{
return "sint32";
}
std::string typestring (const int64_t *) const
{
return "sint64";
}
std::string typestring (const bool *) const
{
return "uint32";
}
std::string typestring (const std::string *) const
{
return "string";
}
template <class T>
std::string typestring (const T *) const
{
const typename Converter::pb_type *v = 0;
return typestring (v);
}
};
/**
@ -1035,7 +1135,23 @@ public:
*/
std::string create_def () const
{
return std::string (); // @@@
std::map<size_t, std::pair <const PBElementBase *, std::string> > msgs;
collect_messages (msgs);
msgs[oid ()] = std::make_pair (this, make_message_name ());
std::string res = "// created from KLayout proto definition '" + name () + "'\n\n";
res += "syntax = \"proto2\";";
for (auto i = msgs.begin (); i != msgs.end (); ++i) {
std::string entry = i->second.first->create_def (msgs);
if (! entry.empty ()) {
res += "\n";
res += "\n";
res += entry;
}
}
return res;
}
private:
@ -1054,10 +1170,15 @@ private:
// disable base class implementation
}
virtual void finish (const PBElementBase * /*parent*/, PBReaderState &) const
virtual void finish (const PBElementBase *, PBReaderState &) const
{
// disable base class implementation
}
virtual std::string create_def_entry (std::map<size_t, std::pair<const PBElementBase *, std::string> > &) const
{
return std::string ();
}
};
/**
@ -1176,7 +1297,8 @@ private:
template <class Value, class Parent>
struct PBMemberDummyReadAdaptor
{
typedef pass_by_ref_tag tag;
typedef pb_pass_by_ref_tag tag;
typedef pb_zero_cardinality_tag cardinality;
PBMemberDummyReadAdaptor ()
{
@ -1207,7 +1329,8 @@ struct PBMemberDummyReadAdaptor
template <class Value, class Parent>
struct PBMemberReadAdaptor
{
typedef pass_by_ref_tag tag;
typedef pb_pass_by_ref_tag tag;
typedef pb_single_cardinality_tag cardinality;
PBMemberReadAdaptor (Value Parent::*member)
: mp_member (member), mp_owner (0), m_done (false)
@ -1245,7 +1368,8 @@ private:
template <class Value, class Parent>
struct PBMemberAccRefReadAdaptor
{
typedef pass_by_ref_tag tag;
typedef pb_pass_by_ref_tag tag;
typedef pb_single_cardinality_tag cardinality;
PBMemberAccRefReadAdaptor (const Value &(Parent::*member) () const)
: mp_member (member), mp_owner (0), m_done (false)
@ -1283,7 +1407,8 @@ private:
template <class Value, class Parent>
struct PBMemberAccReadAdaptor
{
typedef pass_by_value_tag tag;
typedef pb_pass_by_value_tag tag;
typedef pb_single_cardinality_tag cardinality;
PBMemberAccReadAdaptor (Value (Parent::*member) () const)
: mp_member (member), mp_owner (0), m_done (false)
@ -1321,7 +1446,8 @@ private:
template <class Value, class Iter, class Parent>
struct PBMemberIterReadAdaptor
{
typedef pass_by_ref_tag tag;
typedef pb_pass_by_ref_tag tag;
typedef pb_many_cardinality_tag cardinality;
PBMemberIterReadAdaptor (Iter (Parent::*begin) () const, Iter (Parent::*end) () const)
: mp_begin (begin), mp_end (end)

View File

@ -205,15 +205,16 @@ struct Root {
const Child &get_child () const { return m_child; }
};
static tl::PBElementList child_struct =
tl::pb_make_member (&Child::txt, "txt", 1) +
tl::pb_make_member (&Child::d, "d", 2) +
tl::pb_make_element (&Child::begin_children, &Child::end_children, &Child::add_child, "children", 3, &child_struct);
static tl::PBElementList child_struct ("Child",
tl::pb_make_member (&Child::txt, "txt", 1) +
tl::pb_make_member (&Child::d, "d", 2) +
tl::pb_make_element (&Child::begin_children, &Child::end_children, &Child::add_child, "children", 3, &child_struct)
);
static tl::PBStruct<Root> structure ("pbtest-struct", 88888888,
tl::pb_make_member (&Root::begin_subs, &Root::end_subs, &Root::add_sub, "sub", 1) +
tl::pb_make_member (&Root::begin_isubs, &Root::end_isubs, &Root::add_isub, "isub", 2) +
tl::pb_make_element (&Root::begin_children, &Root::end_children, &Root::add_child, "childredn", 3, &child_struct) +
tl::pb_make_element (&Root::begin_children, &Root::end_children, &Root::add_child, "children", 3, &child_struct) +
tl::pb_make_element (&Root::get_child, &Root::set_child, "child", 4, &child_struct) +
tl::pb_make_member (&Root::m, "m", 5) +
tl::pb_make_member (&Root::get_mi, &Root::set_mi, "mi", 6) +
@ -608,3 +609,27 @@ TEST (108_InvalidType)
EXPECT_EQ (std::string (error, 0, 34), "Expected a VARINT or I32 wire type");
}
TEST (109_DumpSpec)
{
EXPECT_EQ (structure.create_def (),
"// created from KLayout proto definition 'pbtest-struct'\n"
"\n"
"syntax = \"proto2\";\n"
"\n"
"message Child {\n"
" optional string txt = 1;\n"
" optional double d = 2;\n"
" repeated Child children = 3;\n"
"}\n"
"\n"
"message PbtestStruct {\n"
" repeated double sub = 1;\n"
" repeated sint32 isub = 2;\n"
" repeated Child children = 3;\n"
" optional Child child = 4;\n"
" optional sint64 m = 5;\n"
" optional sint32 mi = 6;\n"
" optional uint32 b = 7;\n"
"}"
);
}