Names and Name Spaces

Quex follows an object oriented design. Early versions of Quex only produced C++ code. When plain C support was added a consistent naming and function call scheme had to be introduced. This was necessary so that the large majority of code could be shared between C++ and plain C.

C does not support name spaces and it does not support member functions. The rule that associates functions and object names in C with their C++ counterparts is described in the present section.

Let a C++ member function member in class class, an object object and a global function function be defined in some name space X0::X1::X2. Then their C++ code would look as follows:

namespace X0 {
    namespace X1 {
        namespace X2 {
            class::member(arg_0, arg_1, ... arg_i);
            sometype   object;
            void       function(void);
        }
    }
}

The C implementation of those would be

X0_X1_X2_class__member(X0_X1_X2_class* me, arg_0, arg_1, ... arg_i);
X0_X1_X2_sometype   X0_X1_X2_object;
void                X0_X1_X2_function(void);

which follows the well established naming scheme of ‘from general to specific’. That is, from the outermost name space to the deepest, then the class name, if there is one, and then the function or object name itself. Member functions from C++ are implemented as normal functions in ‘C’ where the correspondent C function receives a pointer to the class (struct) as the first argument. All involved names are separated by underscore. The most prominent classes in a lexical analyzer generated by Quex are the ones of the analyzer and the token.

The name spaces and classes of the analyzer and the token can be specified by the command line arguments --analyzer-class and --token-class. See the command line section. For example a command line:

> quex ... --analyzer-class My::Lexer --token-class Our::Token ...

specifies that the lexical analyzer’s class name is Lexer which is defined in namespace My. The token’s class name in Token and it is defined in namespace Token. In C++ member functions can be called as usual. For example a given analyzer qlex and a token may be used with their member functions receive() and pretty_char_text() by

My::Lexer   qlex(...);
Our::Token* token_p = (void*)0;

qlex.receive(&tok);
...
printf("%s\n", tok.pretty_char_text());

which is simply how things are dealt with in C++. The correspondent code fragment in C is

My_Lexer   qlex;
Our_Token* token_p = (void*)0;

My_Lexer_contruct(&qlex, ...);

MyLexer_receive(&qlex, token_p);
...
printf("%s\n", Our_Token_pretty_char_text(token_p, ...));

As can be seen, the plain C code is a little more complicated than the C++ code. However, once the aforementioned name association is understood, the function calls are very intuitive.

Hint

Whenever it is unclear, to what a macro expands, small C/C++ programs such as the example following may help.

#include "MyLexer"
#include <stdio.h>

#define __UNDECEIVE(X)  "\n" #X "\n\n"
#define UNDECEIVE(X)    __UNDECEIVE(X)

int main(int argc, char** argv) {
    printf(UNDECEIVE(QUEX_NAME(receive)));
    return 0;
}

Saving the above code in ‘undeceive.cpp’, compiling and executing it shows the result of the expansion of QUEX_NAME(receive). Something like the following could be expected:

> g++ undeceive.cpp MyLexer.cpp -o undeceive
> ./undeceive

MyLexer_receive

>

It may be necessary to link with the generated lexical analyzer engine’s sources. They contains an implementation of LexemeNull which would else not be referenced. In critical cases, it is advisable to run the GNU Compiler with the ‘-E’ over the generated code, in order to see whether all macros are expanded as expected.