I'm trying to get clang::CompilerInstance to parse a source file which contains an include, but I can't figure out how to get it to actually find the included header. Here is my set up:
std::unique_ptr<clang::CompilerInstance> ci(new clang::CompilerInstance());
ci->createDiagnostics();
LLVMInitializeARMTarget();
LLVMInitializeARMTargetMC();
LLVMInitializeARMAsmPrinter();
LLVMInitializeARMAsmParser();
std::shared_ptr<clang::TargetOptions> options(new clang::TargetOptions);
options->Triple = "arm-v7m-unknown-none-eabi";
options->CPU = "cortex-m3";
clang::TargetInfo *targetInfo = clang::TargetInfo::CreateTargetInfo(ci->getDiagnostics(), options);
ci->setTarget(targetInfo);
ci->createFileManager();
ci->createSourceManager(ci->getFileManager());
NSURL *sysrootURL = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:#"Compiler/basalt"];
NSURL *includeURL = [sysrootURL URLByAppendingPathComponent:#"include"];
ci->createPreprocessor(clang::TranslationUnitKind::TU_Complete);
ci->getPreprocessorOpts().UsePredefines = false;
// Header searcher
llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> hso(new clang::HeaderSearchOptions());
hso->UseBuiltinIncludes = false;
hso->UseStandardSystemIncludes = false;
hso->UseStandardCXXIncludes = false;
hso->Sysroot = [[includeURL path] UTF8String];
clang::HeaderSearch headerSearch(hso, ci->getSourceManager(), ci->getDiagnostics(), ci->getLangOpts(), targetInfo);
headerSearch.AddSearchPath(clang::DirectoryLookup(ci->getFileManager().getDirectory([[includeURL path] UTF8String]), clang::SrcMgr::C_System, false), true);
clang::InitializePreprocessor(ci->getPreprocessor(), ci->getPreprocessorOpts(), ci->getFrontendOpts());
// Main file
const clang::FileEntry *file = ci->getFileManager().getFile([[[_url URLByAppendingPathComponent:#"src/main.c"] path] UTF8String]);
ci->getSourceManager().setMainFileID(ci->getSourceManager().createFileID(file, clang::SourceLocation(), clang::SrcMgr::C_User));
ci->getPreprocessor().EnterMainSourceFile();
ci->getDiagnosticClient().BeginSourceFile(ci->getLangOpts(), &ci->getPreprocessor());
clang::Token tok;
do {
ci->getPreprocessor().Lex(tok);
if(ci->getDiagnostics().hasErrorOccurred())
break;
ci->getPreprocessor().DumpToken(tok);
std::cerr << std::endl;
} while(tok.isNot(clang::tok::eof));
ci->getDiagnosticClient().EndSourceFile();
The paths are definitely 100% correct and checked them over and over again. It all works up until the point the source code I throw at it contains something like #include <foobar.h>, in which case it will fail with error 'foobar.h' file not found, even though foobar.h is definitely there. I feel like I'm missing something really obvious here. Any pointers into the right direction?
Firstly, drop using CompilerInstance- the ownership semantics are so bad it's practically unusable (unless they fixed that in 3.6 with unique_ptr). It's easier to simply make the components yourself.
Secondly, yes, you have to do it yourself. Here's a verbatim excerpt from my own project that uses Clang:
clang::HeaderSearch hs(/*params*/);
// WHY AM I DOING THIS MYSELF CLANG
// CAN'T YOU READ YOUR OWN CONSTRUCTOR PARAMETERS AND OPTIONS STRUCTS?
std::vector<clang::DirectoryLookup> lookups;
for (auto entry : opts.HeaderSearchOptions->UserEntries) {
auto lookup = clang::DirectoryLookup(FileManager.getDirectory(entry.Path), clang::SrcMgr::CharacteristicKind::C_System, false);
if (!lookup.getDir())
throw SpecificError<ClangCouldNotInterpretPath>(a, where, "Clang could not interpret path " + entry.Path);
lookups.push_back(lookup);
}
hs.SetSearchPaths(lookups, 0, 0, true);
Related
I'm trying to get a script compiled with Roslyn but whatever I do it keeps complaining about this, at this moment I tried almost everything and have no idea what to try next.
This is the error I get :
'(11,7): error CS0246: The type or namespace name '' could not be found (are you missing a using directive or an assembly reference?)'
It is complaining that the namespace can't be found
I already added the references to the file and even added the assemblies to the InteractiveAssemblyLoader but still no luck.
try
{
#if (DEBUG)
var metadataReferences = ApplicationSettings.Instance.GetValuesAsStringList("Script editor", "MetadataReferences_VOP");
#else
var metadataReferences = ApplicationSettings.Instance.GetValuesAsStringList("Script editor", "MetadataReferences");
#endif
var scriptOptions = ScriptOptions.Default.WithEmitDebugInformation(true);
scriptOptions.AddReferences(metadataReferences);
using var assemblyLoader = new InteractiveAssemblyLoader();
foreach(var reference in metadataReferences)
assemblyLoader.RegisterDependency(Assembly.Load(File.ReadAllBytes(reference)));
scriptRunner = CSharpScript
.Create<string>(script, scriptOptions, typeof(MainGlobals), assemblyLoader)
.ContinueWith("new NewEmail().Main(Email)")
.CreateDelegate();
exception = null;
return true;
}
catch (Exception e)
{
exception = e;
scriptRunner = null;
return false;
}
Is there somebody that has an idea about what to try next?
It seems that the AddReferences returns a new scriptOptions object that is the one with the added references :-)
So the solution was easy, just do this
scriptOptions = scriptOptions.AddReferences(metadataReferences);
I'm pretty new to using XML in C++ and i'm trying to parse a list of files to download.
THe XML file I'm using is generated via PHP and looks like this :
<?xml version="1.0"?>
<FileList>
<File Name="xxx" Path="xxx" MD5="xxx" SHA1="xxx"/>
</FileList>
The code I'm using in C++ is the following, which I came up using some online tutorials (it's included in some global function):
tinyxml2::XMLDocument doc;
doc.LoadFile("file_listing.xml");
tinyxml2::XMLNode* pRoot = doc.FirstChild();
tinyxml2::XMLElement* pElement = pRoot->FirstChildElement("FileList");
if (pRoot == nullptr)
{
QString text = QString::fromLocal8Bit("Error text in french");
//other stuff
}
else
{
tinyxml2::XMLElement* pListElement = pElement->FirstChildElement("File");
while (pListElement != nullptr)
{
QString pathAttr = QString::fromStdString(pListElement->Attribute("Path"));
QString md5Attr = QString:: fromStdString(pListElement->Attribute("MD5"));
QString sha1Attr = QString::fromStdString(pListElement->Attribute("SHA1"));
QString currentPath = pathAttr.remove("path");
QString currentMd5 = this->fileChecksum(currentPath, QCryptographicHash::Md5);
QString currentSha1 = this->fileChecksum(currentPath, QCryptographicHash::Sha1);
QFile currentFile(currentPath);
if (md5Attr != currentMd5 || sha1Attr != currentSha1 || !currentFile.exists())
{
QString url = "url" + currentPath;
this->downloadFile(url);
}
pListElement = pListElement->NextSiblingElement("File");
}
Problem is, I get an error like "Access violation, this was nullptr" on the following line :
tinyxml2::XMLElement* pListElement = pElement->FirstChildElement("File");
Since I'm far from a pro when it comes to coding and I already searched the internet up and down, I hope that someone here can provide me some pointers.
Have a good day, folks.
I don't know if you have C++17 available, but you can remove a lot of noise by using auto* and if-init-expressions (or rely on the fact that pointers can be implicitly converted to boolean values.)
The main issue with your code is you were not using XMLElement* but instead a XMLNode. The function tinyxml2::XMLDocument::RootElement() automatically gets the top-most element for you.
Because you have an xml declaration at the top, FirstChild returns that...which doesn't have any children, so the rest of the code fails.
By using RootElement tinyxml knows to skip any leading non-element nodes (comments, doctypes, etc.) and give you <FileList> instead.
tinyxml2::XMLDocument doc;
auto err = doc.LoadFile("file_listing.xml");
if(err != tinyxml2::XML_SUCCESS) {
//Could not load file. Handle appropriately.
} else {
if(auto* pRoot = doc.RootElement(); pRoot == nullptr) {
QString text = QString::fromLocal8Bit("Error text in french");
//other stuff
} else {
for(auto* pListElement = pRoot->FirstChildElement("File");
pListElement != nullptr;
pListElement = pListElement->NextSiblingElement("File"))
{
QString pathAttr = QString::fromStdString(pListElement->Attribute("Path"));
QString md5Attr = QString:: fromStdString(pListElement->Attribute("MD5"));
QString sha1Attr = QString::fromStdString(pListElement->Attribute("SHA1"));
QString currentPath = pathAttr.remove("path");
QString currentMd5 = this->fileChecksum(currentPath, QCryptographicHash::Md5);
QString currentSha1 = this->fileChecksum(currentPath, QCryptographicHash::Sha1);
QFile currentFile(currentPath);
if(md5Attr != currentMd5 || sha1Attr != currentSha1 || !currentFile.exists()) {
QString url = "url" + currentPath;
this->downloadFile(url);
}
}
}
}
According to the reference for tinyxml2::XMLNodeFirstChild():
Get the first child node, or null if none exists.
This line will therefore get the root node:
tinyxml2::XMLNode* pRoot = doc.FirstChild();
Meaning when you attempt to find a FileList node within the root node it returns null.
To avoid the access violation, check your pointers are valid before using them. There is an if check for pRoot but the line immediately before it tries to call a function on pRoot. There is no if check for pElement so this is why you get an access violation. As well as checking pointers are valid, consider adding else blocks with logging to say what went wrong (e.g. "could not find element X"). This will help you in the long run - XML parsing is a pain, even with a library like Tinyxml, there are always teething problems like this, so getting into the habit of checki g pointers and logging out helpful messages will definitely pay off.
I know how to create a complete dom from an xml file just using XercesDOMParser:
xercesc::XercesDOMParser parser = new xercesc::XercesDOMParser();
parser->parse(path_to_my_file);
parser->getDocument(); // From here on I can access all nodes and do whatever i want
Well, that works... but what if I'd want to parse a string? Something like
std::string myxml = "<root>...</root>";
xercesc::XercesDOMParser parser = new xercesc::XercesDOMParser();
parser->parse(myxml);
parser->getDocument(); // From here on I can access all nodes and do whatever i want
I'm using version 3. Looking inside the AbstractDOMParser I see that parse method and its overloaded versions, only parse files.
How can I parse from a string?
Create a MemBufInputSource and parse that:
xercesc::MemBufInputSource myxml_buf(myxml.c_str(), myxml.size(),
"myxml (in memory)");
parser->parse(myxml_buf);
Use the following overload of XercesDOMParser::parse():
void XercesDOMParser::parse(const InputSource& source);
passing it a MemBufInputSource:
MemBufInputSource src((const XMLByte*)myxml.c_str(), myxml.length(), "dummy", false);
parser->parse(src);
Im doing it another way. If this is incorrect, please tell me why. It seems to work.
This is what parse expects:
DOMDocument* DOMLSParser::parse(const DOMLSInput * source )
So you need to put in a DOMLSInput instead of a an InputSource:
xercesc::DOMImplementation * impl = xercesc::DOMImplementation::getImplementation();
xercesc::DOMLSParser *parser = (xercesc::DOMImplementationLS*)impl)->createLSParser(xercesc::DOMImplementation::MODE_SYNCHRONOUS, 0);
xercesc::DOMDocument *doc;
xercesc::Wrapper4InputSource source (new xercesc::MemBufInputSource((const XMLByte *) (myxml.c_str()), myxml.size(), "A name");
parser->parse(&source);
You may use MemBufInputSource as found in the xercesc/framework/MemBufInputSource.cpp, and the header file, MemBufInputSource.hpp contains extensive documentation, as similar to answers above:
#include <xercesc/framework/MemBufInputSource.hpp>
char* myXMLBufString = "<root>hello xml</root>";
MemBufInputSource xmlBuf((const XMLByte*)myXMLBufString, 23, "myXMLBufName", false);
But take note, this doesn't seem to work unless you first initialize the system, as below (taken from the xerces-c-3.2.3/samples/src/SAX2Count/SAX2Count.cpp)
bool recognizeNEL = false;
char localeStr[64];
memset(localeStr, 0, sizeof localeStr);
// Initialize the XML4C2 system
try {
if (strlen(localeStr)) {
XMLPlatformUtils::Initialize(localeStr);
} else {
XMLPlatformUtils::Initialize();
}
if (recognizeNEL) {
XMLPlatformUtils::recognizeNEL(recognizeNEL);
}
} catch (const XMLException& toCatch) {
XERCES_STD_QUALIFIER cerr << "Error during initialization! Message:\n"
<< StrX(toCatch.getMessage()) << XERCES_STD_QUALIFIER endl;
return 1;
}
Of course reading a file wouldn't require thinking about this type of prep since you just pass a file path to the program which the parser takes. So for those experiencing seg faults, this could be the answer.
I'm really suffering through the WinRT Windows::Storage namespace with all it's asyncronousness.
I have the following private members in my header file:
//Members for copying the SQLite db file
Platform::String^ m_dbName;
Windows::Storage::StorageFolder^ m_localFolder;
Windows::Storage::StorageFolder^ m_installFolder;
Windows::Storage::StorageFile^ m_dbFile;
And I have the following code block in my implementation file:
//Make sure the SQLite Database is in ms-appdata:///local/
m_dbName = L"DynamicSimulations.db";
m_localFolder = ApplicationData::Current->LocalFolder;
m_installFolder = Windows::ApplicationModel::Package::Current->InstalledLocation;
auto getLocalFileOp = m_localFolder->GetFileAsync(m_dbName);
getLocalFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([this](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status)
{
m_dbFile = operation->GetResults();
if(m_dbFile == nullptr)
{
auto getInstalledFileOp = m_installFolder->GetFileAsync(m_dbName);
getInstalledFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([this](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status)
{
m_dbFile = operation->GetResults();
m_dbFile->CopyAsync(m_localFolder, m_dbName);
});
}
});
I get a memory access violation when it gets to m_dbFile = operation->GetResults();
What am I missing here? I come from a c# background in which this is really easy stuff to do :/
I've tried using '.then' instead of registering the event but I couldn't even get those to compile.
thank you for your help!
If you are interested in the WinRT solution, here it is:
It seems all you want to do is to copy the DB file from the installed location into the local folder. For that the following code should suffice:
//Make sure the SQLite Database is in ms-appdata:///local/
m_dbName = L"DynamicSimulations.db";
m_localFolder = ApplicationData::Current->LocalFolder;
m_installFolder = Windows::ApplicationModel::Package::Current->InstalledLocation;
create_task(m_installFolder->GetFileAsync(m_dbName)).then([this](StorageFile^ file)
{
create_task(file->CopyAsync(m_localFolder, m_dbName)).then([this](StorageFile^ copiedFile)
{
// do something with copiedFile
});
});
I've tried this thing before. Don't do this:
if(m_dbFile == nullptr)
Instead verify the value of "status".
if(status == AsyncStatus::Error)
I know how to create a complete dom from an xml file just using XercesDOMParser:
xercesc::XercesDOMParser parser = new xercesc::XercesDOMParser();
parser->parse(path_to_my_file);
parser->getDocument(); // From here on I can access all nodes and do whatever i want
Well, that works... but what if I'd want to parse a string? Something like
std::string myxml = "<root>...</root>";
xercesc::XercesDOMParser parser = new xercesc::XercesDOMParser();
parser->parse(myxml);
parser->getDocument(); // From here on I can access all nodes and do whatever i want
I'm using version 3. Looking inside the AbstractDOMParser I see that parse method and its overloaded versions, only parse files.
How can I parse from a string?
Create a MemBufInputSource and parse that:
xercesc::MemBufInputSource myxml_buf(myxml.c_str(), myxml.size(),
"myxml (in memory)");
parser->parse(myxml_buf);
Use the following overload of XercesDOMParser::parse():
void XercesDOMParser::parse(const InputSource& source);
passing it a MemBufInputSource:
MemBufInputSource src((const XMLByte*)myxml.c_str(), myxml.length(), "dummy", false);
parser->parse(src);
Im doing it another way. If this is incorrect, please tell me why. It seems to work.
This is what parse expects:
DOMDocument* DOMLSParser::parse(const DOMLSInput * source )
So you need to put in a DOMLSInput instead of a an InputSource:
xercesc::DOMImplementation * impl = xercesc::DOMImplementation::getImplementation();
xercesc::DOMLSParser *parser = (xercesc::DOMImplementationLS*)impl)->createLSParser(xercesc::DOMImplementation::MODE_SYNCHRONOUS, 0);
xercesc::DOMDocument *doc;
xercesc::Wrapper4InputSource source (new xercesc::MemBufInputSource((const XMLByte *) (myxml.c_str()), myxml.size(), "A name");
parser->parse(&source);
You may use MemBufInputSource as found in the xercesc/framework/MemBufInputSource.cpp, and the header file, MemBufInputSource.hpp contains extensive documentation, as similar to answers above:
#include <xercesc/framework/MemBufInputSource.hpp>
char* myXMLBufString = "<root>hello xml</root>";
MemBufInputSource xmlBuf((const XMLByte*)myXMLBufString, 23, "myXMLBufName", false);
But take note, this doesn't seem to work unless you first initialize the system, as below (taken from the xerces-c-3.2.3/samples/src/SAX2Count/SAX2Count.cpp)
bool recognizeNEL = false;
char localeStr[64];
memset(localeStr, 0, sizeof localeStr);
// Initialize the XML4C2 system
try {
if (strlen(localeStr)) {
XMLPlatformUtils::Initialize(localeStr);
} else {
XMLPlatformUtils::Initialize();
}
if (recognizeNEL) {
XMLPlatformUtils::recognizeNEL(recognizeNEL);
}
} catch (const XMLException& toCatch) {
XERCES_STD_QUALIFIER cerr << "Error during initialization! Message:\n"
<< StrX(toCatch.getMessage()) << XERCES_STD_QUALIFIER endl;
return 1;
}
Of course reading a file wouldn't require thinking about this type of prep since you just pass a file path to the program which the parser takes. So for those experiencing seg faults, this could be the answer.