The twentifier header translator
twentifier is a tool that translates C++-like headers containing class declarations
into MMXX-enabled headers that declare MMXX-enabled classes. It
is a simple translator: it can only handle a small subset of the
C++ grammar, and specifically disallows constructs that cannot
be converted into MMXX declarations (though it isn't smart enough
to disallow all such constructs.) Using it is fully optional; its intent is merely
to make it easier to create MMXX-enabled headers, particularly
for the beginning user.
For advanced users and applications, it can still be useful in
that many people find C++-style declarations easier to read than
their MMXX counterparts. For those situations where twentifier is unable to cope with a particular situation or construct, escape
sequences can be used to force it to produce the correct output:
the #quote { ... #} construct bypasses the translation and places the enclosed text
directly into the output MMXX header, and the %inhibit { ... } construct forces twentifier to witness the enclosed declarations (usually typedefs and simplified forward declarations whose true declarations
are present in other headers) without sending them to output.
Here are some of the major restrictions on what may be in a twentifier
input file. Many of these restrictions reflect what is and isn't
expressible in MMXX's header grammar, however occasionally twentifier
will allow constructs that are indeed not allowed or reccomendable.
Please note that this list is not exhaustive:
- Outside of class declarations, only typedefs and forward class/enum/union/class
template declarations may be present (no global functions, no
global variables, no function templates.).
- No in-place function bodies are allowed.
- Multiple declarators are only allowed in member variable declarations
(e.g. int foo[4], bar; is OK, int foo(), bar; and typedef int foo, *bar; are not.)
- Function pointer types are only allowed in typedefs.
- Namespaces are not supported (however, scoped types are allowed
in declarations, e.g. std::size_t.)
- Inside of class declarations, method-less member structs and enums
are OK, member structs with methods, member classes and unions
are not.
- Unless a method is private, any default parameters will be discarded
as part of the translation.
- The only throw-declaration allowed is throw(). If used with a non-private constructor, it will be discarded
as part of the translation.
- A public virtual destructor is always implicitly declared, so
none needs be declared explicitly.
- (Almost ironically) preprocessor directives and macro expansion
are not supported. However, the %include directive may be used in lieu of #include, and the #quote directive can be used to escape other preprocessor directives.
- C- and C++-style comments are supported, and will be forwarded
to output at roughly the same position where the occur in the
input (at present, they're bound to the location of the following
semicolon.)
When twentifier encounters certain constructs that cannot yet be expressed in
MMXX's header grammar, it will place them in the output class'
private declarations section, guarded so that they're only available
to the class' implementation. This is presently the case with
typedefs and overloaded operators. Once the MMXX grammar is extended
to allow these constructs, twentifier will also be revised accordingly.
Because of all these restrictions, the best way to use twentifier
is to either refrain from using it for sophisticated classes,
or to closely monitor its output to make sure the it's correct,
while availing oneself of the #quote and %inhibit constructs to fix any problems along the way.
Here are the special twentifier directives:
- #quote { ... #}
- This directive sends the enclosed text (which may span multiple
lines) directly to output. twentifier ensures that the text is appropriately surrounded by preprocessor
guards and, if the directive occurs inside a class declaration,
that its default access specifier is private: and that it's only available to the module where the class is
implemented.
- %inhibit { ... }
- This directive interprets the enclosed text for its side effects
(e.g. typedefs and class/enum/union/struct/template class forward
declarations) but does not send it to output.
- %optional
- This can be prefixed as a qualifier to member methods to designate
them as optional to the runtime linker for the purposes of revision
management (e.g. by omitting the Req method attribute.) For example, %optional virtual void foo(int);
- %header header_name ;
- This directive specifies the name of the current header minus
any .h/.mxh suffix. Note that the name should not be quoted. At present, this is only used for the purpose of naming
the preprocessor guard macro. Note that this directive should really be used - twentifier will not default to the name of its input file, but rather to
a randomly-generated identifier.
- %include header_name ;
- This directive generates appropriate preprocessor directives to
#include the named header as part of the output header's preamble (it
does not actually process the contents of the file.) Note that
the name should not be quoted with either <...> or "..." and that an .h suffix is implicitly added so that none should be given. twentifier assumes that the header in question is a MMXX-enabled header
- as such it will wrap the #include directive itself with preprocessor guards that follow the MMXX
guard macro naming convention (if it's another kind of header,
the guards won't matter.) Also note that all the %include directives are brought up to the beginning of the output file.
Since twentifier always uses "...", this directive is not suitable for system headers. The latter
may still be included by using #quote { #include <...> #} at the beginning of the file.
It's important to remember that the MMXX tools assume that all MMXX-enabled headers are freestanding, meaning that they must include any header whose declarations
they rely on, with the exception of the prefix file. The mmxxsupgen tool relies heavily on this assumption.
twentifier's input files use the .mxh suffix by convention, and its output files default to the classic
.h suffix.
Here are example input and output files:
Input:
%header MyHeader;
template <class Z> class Vector;
%include BingoHeader;
%inhibit {
typedef Vector<int> IntVector;
class Bingo;
}
#quote {
typedef char *vodzilla;
#}
class MyClass : public Bingo {
public:
struct stuff {
int elem1;
void *elem2;
char elem3[256];
};
enum famous_mafiosi {
oprah,
cher,
bill = 1000,
hillary,
gates = 0x23842UL,
ballmer,
herman = pee_wee
};
inline MyClass(int, double);
virtual ~MyClass();
static void Compute(const char *foo, Vector<float> &bar, double &aardvark) throw();
/* we'll get a warning here, and this will be made private
(temporarily, till the next MMXX release) */
inline MyClass & operator = (const MyClass &);
protected:
virtual int hello() const;
const int *&adriatic, mediterranean[2], *baltic;
virtual int pure_indeed() = 0;
private:
char *mycstring;
IntVector myintvector;
};
Output:
#if !defined(__MYHEADER__) || defined(_MMXX_BREWING_)
#define __MYHEADER__ 1
#ifndef _MMXX_BREWING_
#ifndef __BINGOHEADER__
#include "BingoHeader.h"
#endif /* #ifndef __BINGOHEADER__ */
template <class Z > class Vector;
/* =-= begin quote: =-= */
typedef char *vodzilla;
/* =-= end quote =-= */
#include "mmxxapi.h"
#endif /* #ifndef _MMXX_BREWING_ */
MMXX_CLASS_1_0(MyClass,public,Bingo)
MMXX_STRUCT_START(MyClass,stuff)
MMXX_STRUCT_FIELD(MyClass,stuff, int,elem1)
MMXX_STRUCT_FIELD(MyClass,stuff, void*,elem2)
MMXX_STRUCT_ARRAY(MyClass,stuff, char,elem3,256)
MMXX_STRUCT_END(MyClass,stuff)
MMXX_ENUM_START(MyClass,famous_mafiosi)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, oprah,0)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, cher,1)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, bill,1000)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, hillary,1001)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, gates,0x23842UL)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, ballmer,0x23842UL+1)
MMXX_ENUM_TUPLE(MyClass,famous_mafiosi, herman,pee_wee)
MMXX_ENUM_END(MyClass,famous_mafiosi)
// Warning: dropping 'inline' from MyClass::MyClass
MMXX_CONS_2( Pub, MyClass,Req,
1,(Bingo), int,p33892634,
double,pa86fc21c)
// public: inline MyClass(int, double);
// Warning: public virtual destructor is always implicitly declared
// public: virtual ~MyClass();
MMXX_STATIC_3N( Pub, MyClass,Req,
Compute, const char*,foo,
Vector< float >&,bar,
double&,aardvark)
// public: static void Compute(const char* foo, Vector< float >& bar, double& aardvark) throw();
// Warning: making typedef/operator temporarily private
MMXX_METHOD_0RC(ProtV, MyClass,Req,
int, hello )
// protected: virtual int hello()const ;
// Warning: making MyClass::adriatic private
// Warning: making MyClass::mediterranean private
// Warning: making MyClass::baltic private
MMXX_METHOD_0R( ProtPV, MyClass,Req,
int, pure_indeed )
// protected: virtual int pure_indeed() = 0;
#if !defined(_MMXX_BREWING_) && defined(_MMXX_SHADOW_MyClass) && _MMXX_SHADOW_MyClass==0
private:
inline MyClass& operator = (const MyClass&);
/* we'll get a warning here, and this will be made private
(temporarily, till the next MMXX release) */
const int*& adriatic;
const int mediterranean[2];
const int* baltic;
char* mycstring;
IntVector myintvector;
#endif /* #if !defined(_MMXX_BREWING_) && defined(_MMXX_SHADOW_MyClass) && _MMXX_SHADOW_MyClass==0 */
MMXX_CLASS_END(MyClass)
#endif /* #if !defined(__MYHEADER__) || defined(_MMXX_BREWING_) */