Added a new setting required by the C++ runtime.

In order to export generated classes in DLLs we need a way to specify the __declspec setting. This is is usually done via a macro that takes the import or export value. The new parameter (`-export-macro`) allows to specify this, which increases the flexibility of the generated classes.

The C++ target documentation has been extended to describe build specific things, includig this new parameter.
This commit is contained in:
Mike Lischke 2016-12-15 16:44:26 +01:00
parent 50afb22068
commit 428ff28f35
8 changed files with 45 additions and 10 deletions

View File

@ -20,11 +20,13 @@ You will see that there are a whole bunch of files generated by this call. If vi
## Where can I get the runtime?
Once you've generated the lexer and/or parser code, you need to download or build the runtime. Prebuilt C++ runtime binaries for Windows (VS 2013 runtime), OSX and iOS are available on the ANTLR web site:
Once you've generated the lexer and/or parser code, you need to download or build the runtime. Prebuilt C++ runtime binaries for Windows (Visual Studio 2013/2015), OSX/macOS and iOS are available on the ANTLR web site:
* http://www.antlr.org
Use CMake to build a Linux library (works also on OSX, however not for the iOS library). Building your own library on OSX or Windows is trivial, however. Just open the VS or XCode project, select target + arch and build it. Should work out of the box without any additional dependency.
Use CMake to build a Linux library (works also on OSX, however not for the iOS library).
Instead of downloading a prebuilt binary you can also easily build your own library on OSX or Windows. Just use the provided projects for XCode or Visual Studio and build it. Should work out of the box without any additional dependency.
## How do I run the generated lexer and/or parser?
@ -76,6 +78,29 @@ This example assumes your grammar contains a parser rule named `key` for which t
There are a couple of things that only the C++ ANTLR target has to deal with. They are described here.
### Build Aspects
The code generation (by running the ANTLR4 jar) allows to specify 2 values you might find useful for better integration of the generated files into your application (both are optional):
* A **namespace**: use the **`-package`** parameter to specify the namespace you want.
* An **export macro**: especially in VC++ extra work is required to export your classes from a DLL. This is usually accomplished by a macro that has different values depending on whether you are creating the DLL or import it. The ANTLR4 runtime itself also uses one for its classes:
```c++
#ifdef ANTLR4CPP_EXPORTS
#define ANTLR4CPP_PUBLIC __declspec(dllexport)
#else
#ifdef ANTLR4CPP_STATIC
#define ANTLR4CPP_PUBLIC
#else
#define ANTLR4CPP_PUBLIC __declspec(dllimport)
#endif
#endif
```
Just like the `ANTLR4CPP_PUBLIC` macro here you can specify your own one for the generated classes using the **`-export-macro`** parameter.
In order to create a static lib in Visual Studio define the `ANTLR4CPP_STATIC` macro in addition to the project settings that must be set for a static library (if you compile the runtime yourself).
For gcc and clang it is possible to use the `-fvisibility=hidden` setting to hide all symbols except those that are made default-visible (which has been defined for all public classes in the runtime).
### Memory Management
Since C++ has no built-in memory management we need to take extra care. For that we rely mostly on smart pointers, which however might cause time penalties or memory side effects (like cyclic references) if not used with care. Currently however the memory household looks very stable. Generally, when you see a raw pointer in code consider this as being managed elsewehere. You should never try to manage such a pointer (delete, assign to smart pointer etc.).

View File

@ -43,7 +43,7 @@ cppTypeInitMap ::= [
LexerHeader(lexer, atn, actionFuncs, sempredFuncs, superClass = {antlr4::Lexer}) ::= <<
<namedActions.context>
class <lexer.name> : public <superClass> {
class <file.exportMacro> <lexer.name> : public <superClass> {
public:
<if (lexer.tokens)>
enum {
@ -266,7 +266,7 @@ bool <if (r.factory.g.lexer)><lexer.name><else><parser.name><endif>::<r.name>Sem
ParserHeader(parser, funcs, atn, sempredFuncs, superClass = {antlr4::Parser}) ::= <<
<namedActions.context>
class <parser.name> : public <superClass> {
class <file.exportMacro> <parser.name> : public <superClass> {
public:
<if (parser.tokens)>
enum {
@ -526,7 +526,7 @@ LeftRecursiveRuleFunction(currentRule, args, code, locals, ruleCtx, altLabelCtxs
>>
StructDeclHeader(struct, ctorAttrs, attrs, getters, dispatchMethods, interfaces, extensionMembers) ::= <<
class <struct.name> : public <if (contextSuperClass)><contextSuperClass><else>antlr4::ParserRuleContext<endif><if(interfaces)>, <interfaces; separator=", "><endif> {
class <file.exportMacro> <struct.name> : public <if (contextSuperClass)><contextSuperClass><else>antlr4::ParserRuleContext<endif><if(interfaces)>, <interfaces; separator=", "><endif> {
public:
<attrs: {a | <a>;}; separator="\n">
<if (ctorAttrs)><struct.name>(antlr4::ParserRuleContext *parent, size_t invokingState);<endif>
@ -578,7 +578,7 @@ void <parser.name>::<struct.name>::copyFrom(<struct.name> *ctx) {
>>
AltLabelStructDeclHeader(struct, attrs, getters, dispatchMethods) ::= <<
class <struct.name> : public <currentRule.name; format = "cap">Context {
class <file.exportMacro> <struct.name> : public <currentRule.name; format = "cap">Context {
public:
<struct.name>(<currentRule.name; format = "cap">Context *ctx);

View File

@ -132,7 +132,7 @@ namespace <file.genPackage> {
* which can be extended to create a listener which only needs to handle a subset
* of the available methods.
*/
class <file.grammarName>BaseListener : public <file.grammarName>Listener {
class <file.exportMacro> <file.grammarName>BaseListener : public <file.grammarName>Listener {
public:
<namedActions.baselistenerdeclarations>
@ -192,7 +192,7 @@ namespace <file.genPackage> {
/**
* This interface defines an abstract listener for a parse tree produced by <file.parserName>.
*/
class <file.grammarName>Listener : public antlr4::tree::ParseTreeListener {
class <file.exportMacro> <file.grammarName>Listener : public antlr4::tree::ParseTreeListener {
public:
<namedActions.listenerdeclarations>
@ -248,7 +248,7 @@ namespace <file.genPackage> {
* This class provides an empty implementation of <file.grammarName>Visitor, which can be
* extended to create a visitor which only needs to handle a subset of the available methods.
*/
class <file.grammarName>BaseVisitor : public <file.grammarName>Visitor {
class <file.exportMacro> <file.grammarName>BaseVisitor : public <file.grammarName>Visitor {
public:
<namedActions.basevisitordeclarations>
@ -304,7 +304,7 @@ VisitorFileHeader(file, header, namedActions) ::= <<
* This class defines an abstract visitor for a parse tree
* produced by <file.parserName>.
*/
class <file.grammarName>Visitor : public antlr4::tree::AbstractParseTreeVisitor {
class <file.exportMacro> <file.grammarName>Visitor : public antlr4::tree::AbstractParseTreeVisitor {
public:
<namedActions.visitordeclarations>

View File

@ -112,6 +112,7 @@ public class Tool {
public boolean gen_visitor = false;
public boolean gen_dependencies = false;
public String genPackage = null;
public String exportMacro = null; // C++ specific, need to allow setting a macro to set declspec for generated classes for VC++.
public Map<String, String> grammarOptions = null;
public boolean warnings_are_errors = false;
public boolean longMessages = false;
@ -128,6 +129,7 @@ public class Tool {
new Option("gen_visitor", "-visitor", "generate parse tree visitor"),
new Option("gen_visitor", "-no-visitor", "don't generate parse tree visitor (default)"),
new Option("genPackage", "-package", OptionArgType.STRING, "specify a package/namespace for the generated code"),
new Option("exportMacro", "-export-macro", OptionArgType.STRING, "C++ only, specify a macro for import/export of generated classes"),
new Option("gen_dependencies", "-depend", "generate file dependencies"),
new Option("", "-D<option>=value", "set/override a grammar-level option"),
new Option("warnings_are_errors", "-Werror", "treat warnings as errors"),

View File

@ -12,6 +12,7 @@ import java.util.Map;
public class LexerFile extends OutputFile {
public String genPackage; // from -package cmd-line
public String exportMacro; // from -export-macro cmd-line
public boolean genListener; // from -listener cmd-line
public boolean genVisitor; // from -visitor cmd-line
@ModelElement public Lexer lexer;
@ -21,6 +22,7 @@ public class LexerFile extends OutputFile {
super(factory, fileName);
namedActions = buildNamedActions(factory.getGrammar());
genPackage = factory.getGrammar().tool.genPackage;
exportMacro = factory.getGrammar().tool.exportMacro;
genListener = factory.getGrammar().tool.gen_listener;
genVisitor = factory.getGrammar().tool.gen_visitor;
}

View File

@ -23,6 +23,7 @@ import java.util.Set;
*/
public class ListenerFile extends OutputFile {
public String genPackage; // from -package cmd-line
public String exportMacro; // from -export-macro cmd-line
public String grammarName;
public String parserName;
/**
@ -61,5 +62,6 @@ public class ListenerFile extends OutputFile {
ActionAST ast = g.namedActions.get("header");
if ( ast!=null ) header = new Action(factory, ast);
genPackage = factory.getGrammar().tool.genPackage;
exportMacro = factory.getGrammar().tool.exportMacro;
}
}

View File

@ -16,6 +16,7 @@ import java.util.Map;
/** */
public class ParserFile extends OutputFile {
public String genPackage; // from -package cmd-line
public String exportMacro ; // from -export-macro cmd-line
public boolean genListener; // from -listener cmd-line
public boolean genVisitor; // from -visitor cmd-line
@ModelElement public Parser parser;
@ -28,6 +29,7 @@ public class ParserFile extends OutputFile {
Grammar g = factory.getGrammar();
namedActions = buildNamedActions(factory.getGrammar());
genPackage = g.tool.genPackage;
exportMacro = g.tool.exportMacro;
// need the below members in the ST for Python, C++
genListener = g.tool.gen_listener;
genVisitor = g.tool.gen_visitor;

View File

@ -20,6 +20,7 @@ import java.util.Set;
public class VisitorFile extends OutputFile {
public String genPackage; // from -package cmd-line
public String exportMacro; // from -export-macro cmd-line
public String grammarName;
public String parserName;
/**
@ -58,5 +59,6 @@ public class VisitorFile extends OutputFile {
ActionAST ast = g.namedActions.get("header");
if ( ast!=null ) header = new Action(factory, ast);
genPackage = factory.getGrammar().tool.genPackage;
exportMacro = factory.getGrammar().tool.exportMacro;
}
}