Problems linking project using parse tree annotations

15 views
Skip to first unread message

Ge Hu

unread,
Mar 28, 2024, 1:29:19 PMMar 28
to antlr-discussion
I've been working my way through the ANTLR4 book while learning the cpp-runtime.  To this end I've been translating all the example in the book from Java to C++.  I've had sucess through chapter 8 doing the implementations on a Rocky 8 Linux system (Rocky 8.9, using ANTLR4-4.13.1 (tool and cpp-runtime), compiler: gcc-8.5.0 and Java openjdk 11.0.22) with the runtime library compiled from source, yielding the following result,

java -Xmx500M -jar /opt/antlr/antlr-4.13.1-complete.jar -Dlanguage=Cpp  JSON.g4 -o ./c++
make -C ./c++
make[1]: Entering directory '/home/******/projects/antlr4-cpp-runtime/08-json2xml/c++'
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og JSONBaseListener.cpp -o JSONBaseListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og JSONLexer.cpp -o JSONLexer.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og JSONListener.cpp -o JSONListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og JSONParser.cpp -o JSONParser.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og XMLEmitter.cpp -o XMLEmitter.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/usr/local/include/antlr4-runtime -Og JSON2XML.cpp -o JSON2XML.o
c++ -L/usr/local/lib JSONBaseListener.o JSONLexer.o JSONListener.o JSONParser.o XMLEmitter.o JSON2XML.o -l:libantlr4-runtime-4.13.1.a -lpthread -o JSON2XML
make[1]: Leaving directory '/home/******/projects/antlr4-cpp-runtime/08-json2xml/c++'

Things worked, so I had a great idea... and this is were everything went sideways, to attempt a build on a Window system.  So I installed got the Cpp runtime code base and built in with visual studio 2019 (version 4.8.09037) on a Window 10 (version 22H2) host, building as both a static library and a dynamic library.  Then I got the code for the JSON to XML converter (that worked on the Linux side) and attempted to compile, and got the result,

C:\SDKs\Antlr4\include\Exceptions.h(13,67): warning C4275: non dll-interface class 'std::exception' used as base for dll-interface class 'antlr4::RuntimeException'
C:\SDKs\Antlr4\include\Exceptions.h(15,17): warning C4251: 'antlr4::RuntimeException::_message': class 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' needs to have dll-interface to be used by clients of class 'antlr4::RuntimeException'

[... 713 similar warnings omitted ...]

C:\SDKs\Antlr4\include\tree\ParseTreeProperty.h(50,29): warning C4251: 'antlr4::tree::ParseTreeProperty<std::string>::_annotations': class 'std::map<antlr4::tree::ParseTree *,V,std::less<antlr4::tree::ParseTree *>,std::allocator<std::pair<antlr4::tree::ParseTree *const ,V>>>' needs to have dll-interface to be used by clients of class 'antlr4::tree::ParseTreeProperty<std::string>'
JSON2XML.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: virtual __cdecl antlr4::tree::ParseTreeProperty<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::~ParseTreeProperty<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??1?$ParseTreeProperty@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@tree@antlr4@@UEAA@XZ) referenced in function "public: virtual __cdecl XMLEmitter::~XMLEmitter(void)" (??1XMLEmitter@@UEAA@XZ)
JSON2XML.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl antlr4::tree::ParseTreeProperty<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::ParseTreeProperty<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??0?$ParseTreeProperty@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@tree@antlr4@@QEAA@XZ) referenced in function "public: __cdecl XMLEmitter::XMLEmitter(void)" (??0XMLEmitter@@QEAA@XZ)
C:\GNUHome\projects\antlr4-cpp-runtime\08-json2xml\c++\x64\Debug\08-jason2xml.exe : fatal error LNK1120: 2 unresolved externals
    716 Warning(s)
    3 Error(s)


Looking at the errors, it appears that they are originating in the file XMLEmitter.cpp, which is shown below:

==== file XMLEmitter.h ====

#ifndef _XMLEmitter_h_
#define _XMLEmitter_h_

#include "antlr4-runtime.h"

#include "JSONBaseListener.h"

#include <string>

using namespace antlr4;
using namespace tree;

class XMLEmitter : public JSONBaseListener
{
public:
  XMLEmitter() { }
  ~XMLEmitter() { }

  void exitJson(JSONParser::JsonContext * /*ctx*/);
  void exitAnObject(JSONParser::AnObjectContext * /*ctx*/);
  void exitEmptyObject(JSONParser::EmptyObjectContext * /*ctx*/);
  void exitArrayOfValues(JSONParser::ArrayOfValuesContext * /*ctx*/);
  void exitEmptyArray(JSONParser::EmptyArrayContext * /*ctx*/);
  void exitPair(JSONParser::PairContext * /*ctx*/);
  void exitString(JSONParser::StringContext * /*ctx*/);
  void exitAtom(JSONParser::AtomContext * /*ctx*/);
  void exitObjectValue(JSONParser::ObjectValueContext * /*ctx*/);
  void exitArrayValue(JSONParser::ArrayValueContext * /*ctx*/);

  std::string getXML(tree::ParseTree* t) { return xml.get(t); }

private:
  ParseTreeProperty<std::string> xml;
  void setXML(tree::ParseTree* ctx, std::string s) { xml.put(ctx, s); }

  std::string stripQuotes(std::string&);
};


==== file XMLEMitter.cpp =====

#include "XMLEmitter.h"

void XMLEmitter::exitJson(JSONParser::JsonContext* ctx)
{
  setXML(ctx, getXML(dynamic_cast<tree::ParseTree*>(ctx)->children[0]));
}


void XMLEmitter::exitAnObject(JSONParser::AnObjectContext* ctx)
{
  std::string buf;
  buf.append(1, '\n');
  for (JSONParser::PairContext* pctx : ctx->pair())
  {
buf.append(getXML(pctx));
  }
 // setXML(ctx, buf);
}


void XMLEmitter::exitEmptyObject(JSONParser::EmptyObjectContext* ctx)
{
  //setXML(ctx, "");
}


void XMLEmitter::exitArrayOfValues(JSONParser::ArrayOfValuesContext* ctx)
{
  std::string buf;
  buf.append(1, '\n');

  for(JSONParser::ValueContext* vctx: ctx->value())
  {
buf.append("<element>");
//buf.append(getXML(vctx));
buf.append("<\\element>");
buf.append(1, '\n');
  }
  ////setXML(ctx, buf);
}


void XMLEmitter::exitEmptyArray(JSONParser::EmptyArrayContext* ctx)
{
//  setXML(ctx, "");
}


void XMLEmitter::exitPair(JSONParser::PairContext* ctx)
{
  std::string s = ctx->STRING()->getText();
  std::string tag = stripQuotes(s);
  JSONParser::ValueContext* vctx = ctx->value();
  //std::string buf = "<" + tag + "\\>" + getXML(vctx) + "</" + tag + ">";
  //setXML(ctx, buf);  
}


void XMLEmitter::exitString(JSONParser::StringContext* ctx)
{
  std::string s = ctx->getText();
 // setXML(ctx, stripQuotes(s));
}


void XMLEmitter::exitAtom(JSONParser::AtomContext* ctx)
{
  //setXML(ctx, ctx->getText());
}


void XMLEmitter::exitObjectValue(JSONParser::ObjectValueContext* ctx)
{
  //setXML(ctx, getXML(ctx->object()));
}


void XMLEmitter::exitArrayValue(JSONParser::ArrayValueContext * ctx)
{
  //setXML(ctx, getXML(ctx->array()));
}


std::string XMLEmitter::stripQuotes(std::string& s)
{
  if(s.empty() || s.at(0) != '"') return s;
  return s.substr(1, s.length()-1);
}


Things I have done to investigate this issue:

a) explicitly add a constructor to ParseTreeProperty.h
b) create a file in the runtime called 'ParseTreeProperty.cpp' that initially contained an include statement for ParseTreePropery.h, and later contain explicit implementations for the constructor and destructor.  In all these cases the constructor was not found.  The destructor was found if I explicit implmented it.  (Note: given that ParseTreeProperty is a header only implementation I did not expect any of these to work -- and I was not surprised.)
c) explicitly add "tree/ParseTreeProperty.h" to XMLEmitter.cpp (would not expect this to work seeing as how ParseTreeProperty.h is included in the umbrella header file) which did nothing.
d) Seeing as how I had a MSYS2 system on the same host, installed antlr4 into the MSYS2 directory tree and installed the runtime library from a MSYS2 package.  attempting to build the JSON to XML converter gave,
$ make
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og JSONBaseListener.cpp -o JSONBaseListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og JSONLexer.cpp -o JSONLexer.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og JSONListener.cpp -o JSONListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og JSONParser.cpp -o JSONParser.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og XMLEmitter.cpp -o XMLEmitter.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/mingw64/include/antlr4-runtime -Og JSON2XML.cpp -o JSON2XML.o
c++ -L/mingw64/lib JSONBaseListener.o JSONLexer.o JSONListener.o JSONParser.o XMLEmitter.o JSON2XML.o -l:libantlr4-runtime.dll.a -lpthread -o JSON2XML
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: XMLEmitter.o: in function `antlr4::tree::ParseTreeProperty<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~ParseTreeProperty()':
C:/msys64/mingw64/include/antlr4-runtime/tree/ParseTreeProperty.h:32:(.text$_ZN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev[_ZN6antlr
44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev]+0x7): undefined reference to `__imp__ZTVN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_s
tringIcSt11char_traitsIcESaIcEEEEE'
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: XMLEmitter.o: in function `antlr4::tree::ParseTreeProperty<std::__cxx11::basi
c_string<char, std::char_traits<char>, std::allocator<char> > >::~ParseTreeProperty()':
C:/msys64/mingw64/include/antlr4-runtime/tree/ParseTreeProperty.h:32:(.text$_ZN10XMLEmitterD1Ev[_ZN10XMLEmitterD1Ev]+0x15): undefined reference to `__imp__ZTVN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE'
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/include/antlr4-runtime/tree/ParseTreeProperty.h:32:(.text$_ZN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED0Ev[_ZN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE
EED0Ev]+0xb): undefined reference to `__imp__ZTVN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE'
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: JSON2XML.o: in function `antlr4::tree::ParseTreeProperty<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::ParseTreeProperty()':
C:/msys64/mingw64/include/antlr4-runtime/tree/ParseTreeProperty.h:30:(.text+0x14b): undefined reference to `__imp__ZTVN6antlr44tree17ParseTreePropertyINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEE'
collect2.exe: error: ld returned 1 exit status

make: *** [makefile:17: JSON2XML] Error 1

which appears to be the same error as observed in the Visual Studio build.

e) install antlr and the runtime library on WSL (running ubuntu) and attempted to build the JSON to XML converted, giving

******@DESKTOP-PCRLQKQ:~/antlr4-cpp-runtime/08-json2xml$ make cpp
java -Xmx500M -jar /opt/antlr4/antlr-4.13.1-complete.jar -Dlanguage=Cpp  JSON.g4 -o ./c++
make -C ./c++
make[1]: Entering directory '/home/******/antlr4-cpp-runtime/08-json2xml/c++'
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONBaseListener.cpp -o JSONBaseListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONLexer.cpp -o JSONLexer.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONListener.cpp -o JSONListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONParser.cpp -o JSONParser.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og XMLEmitter.cpp -o XMLEmitter.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSON2XML.cpp -o JSON2XML.o
c++ -L/home/******/bin/antlr/lib JSONBaseListener.o JSONLexer.o JSONListener.o JSONParser.o XMLEmitter.o JSON2XML.o -l:libantlr4-runtime.a -lpthread -o JSON2XML
make[1]: Leaving directory '/home/******/antlr4-cpp-runtime/08-json2xml/c++'


f) looking at the two sucessfull Linux builds and the two unsucessfull Windows builds, the only difference that is immediately apparent is that in the two Linux builds I am linking to the static library.  So as a test I modified the linking command to use a shared object,

******@DESKTOP-PCRLQKQ:~/antlr4-cpp-runtime/08-json2xml$ make cpp
java -Xmx500M -jar /opt/antlr4/antlr-4.13.1-complete.jar -Dlanguage=Cpp  JSON.g4 -o ./c++
make -C ./c++
make[1]: Entering directory '/home/ghuber/antlr4-cpp-runtime/08-json2xml/c++'
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONBaseListener.cpp -o JSONBaseListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONLexer.cpp -o JSONLexer.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONListener.cpp -o JSONListener.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSONParser.cpp -o JSONParser.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og XMLEmitter.cpp -o XMLEmitter.o
c++ -c -g -std=c++17 -pedantic -Wall -I. -I/home/******/bin/antlr/include/antlr4-runtime -Og JSON2XML.cpp -o JSON2XML.o
c++ -L/home/******/bin/antlr/lib JSONBaseListener.o JSONLexer.o JSONListener.o JSONParser.o XMLEmitter.o JSON2XML.o -lantlr4-runtime -lpthread -o JSON2XML
make[1]: Leaving directory '/home/******/antlr4-cpp-runtime/08-json2xml/c++'


it would appear that the type (static or dynamic) of the runtime library do not matter.

So the questions that I have are,

Several questions here

1.  Of the 716 warnings about half of them are of the form of the first (C4275) which according to Microsoft's documentation can be safely ignored "C4275 can be ignored in Visual C++ if you are deriving from a type in the C++ Standard Library, compiling a debug release (/MTd) and where the compiler error message refers to _Container_base.", given that I only see those errors in a Debug build, and they all related to deriving a class from std::exception.  

Of the remaining warnings (C4251) Microsoft documentation says "This warning happens if a class is marked with __declspec(dllexport) or __declspec(dllimport) and a nonstatic data member that is a member of the class or a member of one of its base classes, has a type that is a class type that isn't marked with __declspec(dllexport) or __declspec(dllimport)."  In the above example, dealing with Exception.h I believe that the fix would be:

  class ANTLR4CPP_PUBLIC RuntimeException : public std::exception {
  private:
    ANTLR4CPP_PUBLIC std::string _message;
  public:
    RuntimeException(const std::string &msg = "");

    virtual const char* what() const noexcept override;
  };

I do know if this is the root of the problems that I am seeing, but I am getting 1408 C4251 warning when I compile to project, of which
16 of them are coming from ParseTreeProperty.h.

2. The linking errors appear to be due to not being able for find a constructor or destructor for ParseTreeProperty<std::string>.  Now the ParseTreeProperty.h file (and it is a header only file) is in the CPP runtime source distribution, but is is not referenced by any files build during the compilation of the runtime.  However it is include by antlr4-runtime.h, so I would expect it to be included in the compilation unit for XMLEmitter.h where is is used.  I fail to see (and it is probably something trivial) why the linker is failing to find definitions for those two functions.  (note in the below code examples, I commented out all reference to where I used the parse node property, xml, to minimize the errors begining displayed.  commenting out the declaration of the property allows the compliation to run to completion without error).

Thanks for reading this far, and any suggestions would be helpful.
Reply all
Reply to author
Forward
0 new messages