📄 writing_a_be
字号:
- The IDL input is preprocessed (in driver/drv_preproc.cc).
- FE initialization stage 1 is done: the scopes stack is created and stored
in the global data variable idl_global->scopes() field (in fe/fe_init.cc).
- BE_init is called to create the generator instance and the returned
instance is stored in the global data variable idl_global->gen() field.
- FE initialization stage 2 is done: the global scope is created, pushed on
the scopes stack and populated with predefined types (in fe/fe_init.cc).
GLOBAL STATE AND ENTRY POINTS
-----------------------------
The CFE has one global variable named idl_global, which stores an instance
of a class IDL_GlobalData as explained below:
The CFE defines a class IDL_GlobalData which defines the global
information used in a specific run of the compiler. IDL_GlobalData is
defined in include/idl_global.hh and implemented in the file
util/utl_global.cc.
Initialization creates an instance of this class and stores it in the value
of the global variable idl_global. Thus, the individual pieces of
information stored in the instance are accessible everywhere.
ERROR HANDLING
--------------
All error handling is defined by a class provided by the CFE, UTL_Error.
This class is defined in include/utl_error.hh and implemented in the file
util/utl_error.cc. The class provides several methods for reporting
specific errors as well as generic error reporting methods taking zero to
three arguments.
The CFE instantiates the class and stores the instance as part of the
global state, accessible as idl_global->err(). Thus, to cause an error
report, you would write code similar to the following:
if (error condition found)
idl_global->err()->specific_error_message(arg1, ..);
or
if (error condition found)
idl_global->err()->generic_error_message(flag, arg1, ..);
The flag argument is one of the predefined error conditions found in the
enum at the head of the UTL_Error class definition. The arguments to the
specific error message routine are defined by the signature of that
routine. The arguments to a generic error message routine are always
instances of AST_Decl.
The running count of errors is accessible as idl_global->err_count(). If
the value returned by this operation is non-zero after the IDL input has
been parsed, the BE is not invoked.
HANDLING OF COMMAND LINE ARGUMENTS
----------------------------------
Defined command line arguments are specified in the document CLI, in this
directory. The CFE calls the required BE API entry point BE_prep_arg to
process arguments passed within a -Wb flag.
REQUIRED ENTRY POINTS SUPPLIED BY A BE
--------------------------------------
The following API entry points must be supplied by a BE in order to
successfully link with the CFE:
extern "C" AST_Generator *BE_init();
Creates an instance of the generator object and returns it. Note
that the global scope is not yet set up and the scopes stack is
empty when this routine is called.
extern "C" void BE_produce();
Called by the compiler main program after the IDL input has been
successfully parsed and processed. The job of this routine is to
carry out the specific function of the BE. The AST is accessible as
the value of idl_global->root().
extern "C" void BE_prep_arg(char *, idl_bool);
Called to process an argument passed in with a -Wb flag. The boolean
will always be FALSE.
extern "C" void BE_abort();
Called when the CFE decides to abort the compilation. Can be used in
a BE to clean up after itself, e.g. remove temporary files or
directories it created while the parse was in progress.
extern "C" void BE_version();
Called when a -V argument is processed. This should produce a
message for the user identifying the BE that is loaded and its
version information.
PART II. WRITING A BACK END
-=========================-
REQUIRED API THAT EACH BE MUST SUPPORT
--------------------------------------
Below are the API entry points that each BE must supply in order to use the
CFE framework. This is a repeat of the BE API section:
extern "C" AST_Generator *BE_init();
Creates an instance of the generator object and returns it. Note
that the scopes stack is still not set up at the time this routine
is called.
extern "C" void BE_produce();
Called by the compiler main program after the IDL input has been
successfully parsed and processed. The job of this routine is to
carry out the specific function of the BE. The AST is accessible as
the value of idl_global->root().
extern "C" void BE_prep_arg(char *, boolean);
Called to process an argument passed in with a -Wb flag. The boolean
will always be FALSE.
extern "C" void BE_abort();
Called when the CFE decides to abort the compilation. Can be used in
a BE to clean up after itself, e.g. remove temporary files or
directories it created while the parse was in progress.
extern "C" void BE_version();
Called when a -V argument is processed. This should produce a
message for the user identifying the BE that is loaded and its
version information.
WHAT FILES TO INCLUDE
---------------------
To use the CFE, each implementation file of your BE must include the
following two header files:
#include <idl.hh>
#include <idl_extern.hh>
Following this, you can include any header files needed by your BE.
HOW TO SUBCLASS THE AST
-----------------------
Your BE may subclass from any of the classes provided by the AST. Your
class should use public virtual inheritance to ensure that only one copy of
the class's data members is present in each instance. Read the section on
HOW TO WRITE CONSTRUCTORS to learn about additional considerations that you
must take into account when writing constructors for your BE classes.
HOW TO SUBCLASS THE GENERATOR TO CREATE BE ENHANCED AST NODES
-------------------------------------------------------------
Your BE subclasses from classes provided by the AST. To ensure that
instances of these classes are constructed when the AST is built, you must
also subclass AST_Generator and return an instance of your subclass from
the call to BE_init.
The AST_Generator class provides operations to create instances of all
classes defined in the AST. For example, the operation to create an
AST_Attribute node is as follows:
AST_Attribute *
AST_Generator::create_attribute(...)
{
return new AST_Attribute(...);
}
In your BE_Generator subclass of AST_Generator, you will override methods
for creation of nodes of all AST classes which you have subclassed. Thus,
if your BE has a class BE_Attribute which is a subclass of AST_Attribute,
your BE_Generator class definition has to override the create_attribute
method to ensure that instances of BE_Attribute are created.
The definition of the overriden operations should call the constructor of
the derived class and return the new node as an instance of the inherited
class. Thus, the implementation of create_attribute is as follows:
AST_Attribute *
BE_Generator::create_attribute(...)
{
return new BE_Attribute(...);
}
The Yacc grammar actions call create_xxx operations on the generator
instance stored in the global variable idl_global->gen() field. By storing
an instance of your derived generator class BE_Generator you ensure that
instances of the BE classes you defined will be created.
HOW TO WRITE CONSTRUCTORS FOR BE CLASSES
----------------------------------------
As mentioned above, the AST uses public virtual inheritance to derive the
AST class hierarchy. This has two important effects on how you write a BE,
specifically how you write constructors for derived BE classes.
First, you must define a default constructor for your BE class, since
your class may be used as a virtual base class of some other class. In that
case the compiler may want to call a default constructor for your class. It
is a good idea to have a default constructor anyway, even if you do not
plan to subclass your BE class, since for most C++ compilers this causes
the code to be smaller. Your default constructor should initialize all
constant data members. Additionally, it may initialize any non-constant
data member whose value must be set before the first time the instance is
used.
Second, the constructor for your BE class must explicitly call all
constructors of virtual base classes which do some useful work. For
example, if a class in the AST from which your BE class inherits, directly
or indirectly, has an initializer for a data member, your BE class's
constructor must call the AST class's constructor. This is discussed
extensively in the C++ ARM.
Below is a list showing how to write constructors for subclasses of each
class provided by the BE. For each AST class we show a definition of a
constructor for a derived class which calls all neccessary constructors on
AST classes:
AST_Argument:
BE_Argument::BE_Argument(AST_Argument::Direction d,
AST_Type *ft,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Argument(d, ft, n, p),
AST_Field(AST_Decl::NT_argument, ft, n, p),
AST_Decl(AST_Decl::NT_argument, n, p)
{
}
AST_Array:
BE_Array::BE_Array(UTL_ScopedName *n,
unsigned long nd,
UTL_ExprList *ds)
: AST_Array(n, nd, ds),
AST_Decl(AST_Decl::NT_array, n, NULL)
{
}
AST_Attribute:
BE_Attribute::BE_Attribute(boolean ro,
AST_Type *ft,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Attribute(ro, ft, n, p),
AST_Field(AST_Decl::NT_attr, ft, n, p),
AST_Decl(AST_Decl::NT_attr, n, p)
{
}
AST_ConcreteType:
BE_ConcreteType::BE_ConcreteType(AST_Decl::NodeType nt,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Decl(nt, n, p)
{
}
AST_Constant:
BE_Constant::BE_Constant(AST_Expression::ExprType t,
AST_Expression *v,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Constant(t, v, n, p),
AST_Decl(AST_Decl::NT_const, n, p)
{
}
AST_Decl:
BE_Decl::BE_Decl(AST_Decl::NodeType nt,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Decl(nt, n, p)
{
}
AST_Enum:
BE_Enum::BE_Enum(UTL_ScopedName *n,
UTL_StrList *p)
: AST_Enum(n, p),
AST_Decl(AST_Decl::NT_enum, n, p),
UTL_Scope(AST_Decl::NT_enum)
{
}
AST_EnumVal:
BE_EnumVal::BE_EnumVal(unsigned long v,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_EnumVal(v, n, p),
AST_Constant(AST_Expression::EV_ulong,
AST_Decl::NT_enum_val,
new AST_Expression(v),
n,
p),
AST_Decl(AST_Decl::NT_enum_val, n, p)
{
}
AST_Exception:
BE_Exception::BE_Exception(UTL_ScopedName *n,
UTL_StrList *p)
: AST_Decl(AST_Decl::NT_except, n, p),
AST_Structure(AST_Decl::NT_except, n, p),
UTL_Scope(AST_Decl::NT_except)
{
}
AST_Field:
BE_Field::BE_Field(AST_Type *ft,
UTL_ScopedName *n,
UTL_StrList *p)
: AST_Field(ft, n, p),
AST_Decl(AST_Decl::NT_field, n, p)
{
}
AST_Interface:
BE_Interface::BE_Interface(UTL_ScopedName *n,
AST_Interface **ih,
long nih,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -