Is there a tool to auto-generate the ostream << operator for a struct or class?
Input (taken from One Debug-Print function to rule them all):
typedef struct ReqCntrlT /* Request control record */
{
int connectionID;
int dbApplID;
char appDescr[MAX_APPDSCR];
int reqID;
int resubmitFlag;
unsigned int resubmitNo;
char VCIver[MAX_VCIVER];
int loginID;
} ReqCntrlT;
Output:
std::ostream& operator <<(std::ostream& os, const ReqCntrlT& r)
{
os << "reqControl { "
<< "\n\tconnectionID: " << r.connectionID
<< "\n\tdbApplID: " << r.dbApplID
<< "\n\tappDescr: " << r.appDescr
<< "\n\treqID: " << r.reqID
<< "\n\tresubmitFlag: " << r.resubmitFlag
<< "\n\tresubmitNo: " << r.resubmitNo
<< "\n\tVCIver: " << r.VCIver
<< "\n\tloginID: " << r.loginID
<< "\n}";
return os;
}
Any tool would be fine, Python / Ruby scripts would be preferred.
What is needed for this is a tool that can parse C++ accurately, enumerate the various classes/structs, determine the and generate your "serializations" on a per class/struct basis, and then park the generated code in the "right place" (presumbably the same scope in which the struct was found). It needs a full preprocessor to handle expansion of directives in real code.
Our DMS Software Reengineering Toolkit with its C++11 front end could do this. DMS enables the construction of custom tools by providing generic parsing/AST building, symbol table construction, flow and custom analysis, transformation and source code regeneration capability. The C++ front enables DMS to parse C++ and build accurate symbol tables, as well as to pretty print modified or new ASTs back to compilable source form. DMS and its C++ front end have been used to carry out massive transformations on C++ code.
You have to explain to DMS what you want to do; seems straightforward to enumerate symbol tables entries, ask if struct/class type declarations, determine scope of the declaration (recorded in the symbol table entry), construct an AST by composing surface syntax patterns, and then apply a transformation to insert the constructed AST.
The core surface syntax patterns needed are those for slots and for the function body:
pattern ostream_on_slot(i:IDENTIFIER):expression =
" << "\n\t" << \tostring\(\i\) << r.\i "; -- tostring is a function that generates "<name>"
pattern ostream_on_struct(i:IDENTIFIER,ostream_on_slots:expression): declaration =
" std::ostream& operator <<(std::ostream& os, const \i& r)
{ os << \tostring\(\i\) << " { " << \ostream_on_slots << "\n}";
return os;
}
One has to compose the individual trees for ostream_on_slot:
pattern compound_ostream(e1:expression, e2:expression): expression
= " \e1 << \e2 ";
With these patterns it is straightforward to enumerate the slots of struct, construct the ostream for the body, and insert that into the overall function for a struct.
There are two main ways to do this:
.. and of course they can be mixed.
I don't have enough knowledge about Clang Python bindings to answer using them, so I will concentrate on metapogramming.
Basically, what you are asking for requires introspection. C++ does not support full introspection, however using metaprogramming tricks (and template matching) it can support a limited subset of introspection technics at compilation time, which is sufficient for our purpose.
In order to easily mix metaprogramming and runtime operation, it's easier to bring a library into play: Boost.Fusion.
If you tweak your structure such that its attributes are descrited in terms of a Boost.Fusion sequence, then you can apply plenty of algorithm on the sequence automatically. Here, an associate sequence is best.
Because we are talking metaprogramming, the map associates a type to a typed value.
You can then iterate over that sequence using for_each.
I'll gloss over the details, simply because it's been a while and I don't remember the syntax involved, but basically the idea is to get to:
// Can be created using Boost.Preprocessor, but makes array types a tad difficult
DECL_ATTRIBUTES((connectionId, int)
(dbApplId, int)
(appDescr, AppDescrType)
...
);
which is syntactic sugar to be declaring the Fusion Map and its associated tags:
struct connectionIdTag {};
struct dbApplIdTag {};
typedef boost::fusion::map<
std::pair<connectionIdTag, int>,
std::pair<dbApplIdTag, int>,
...
> AttributesType;
AttributesType _attributes;
Then, any operation that need be applied on the attributes can be built simply with:
// 1. A predicate:
struct Predicate {
template <typename T, typename U>
void operator()(std::pair<T, U> const&) const { ... }
};
// 2. The for_each function
for_each(_attributes, Predicate());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With