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