Pretty Print Statement to String in Clang - c++

I'm trying to pretty print a Clang statement to a string so that the C/C++ code of the statement is printed. I do this in the following way:
//Generate string and string ostream.
string stmt;
raw_string_ostream stream(stmt);
//Get statement from ASTMatcher and print to ostream.
auto* statement = result.Nodes.getNodeAs<clang::Expr>(types[VAR_STMT]);
statement->printPretty(stream, NULL, PrintingPolicy(LangOptions()));
//Flush ostream buffer.
stream.flush();
cout << statement << endl;
This code compiles and runs fine. However, when I run the following code, I get the address of the statement object printed to the string. For instance, when I run this code, I get the following as output:
0x3ccd598
0x3ccd5b0
0x3ccd728
0x3ccdc88
0x3ccdd08
There isn't really a lot of documentation about printPretty(...) in Clang's documentation so what is the correct way to print the code of a statement to a string?

One solution I found while trying to get this to work is from a Clang developers post from 2013.
Instead of:
//Generate string and string ostream.
string stmt;
raw_string_ostream stream(stmt);
//Get statement from ASTMatcher and print to ostream.
auto* statement = result.Nodes.getNodeAs<clang::Expr>(types[VAR_STMT]);
statement->printPretty(stream, NULL, PrintingPolicy(LangOptions()));
//Flush ostream buffer.
stream.flush();
cout << statement << endl;
My code is now:
//Get the statement from the ASTMatcher
auto *statement = result.Nodes.getNodeAs<clang::Expr>(types[VAR_STMT]);
//Get the source range and manager.
SourceRange range = statement->getSourceRange();
const SourceManager *SM = result.SourceManager;
//Use LLVM's lexer to get source text.
llvm::StringRef ref = Lexer::getSourceText(CharSourceRange::getCharRange(range), *SM, LangOptions());
cout << ref.str() << endl;
This approach does seem to work though I'm not too sure of any potential downsides.

You appear to be confusing yourself because of your similar variable names. You have a variable stmt that is of type string and you have a variable statement that is (presumably) of type clang::Stmt *. The printPretty call is modifying the stream variable, which writes to stmt, and not statement. Then you print statement, the pointer to the clang type. So naturally, a cout call for a pointer type writes the pointer's address.
Change your cout line to write out stmt, and you'll get what you're expecting.

I just figured out that it's possible to print a statement into a string using a raw_string_ostream:
// stmt is the statement you wish to print
string stmtToString(Stmt *stmt){
clang::LangOptions lo;
string out_str;
llvm::raw_string_ostream outstream(out_str);
stmt->printPretty(outstream, NULL, PrintingPolicy(lo));
return out_str;
}

Related

C++ writing to file producing garbage data [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to add data into a text file. However, i see for some reason it produces garbage data. I also notice, it will input the correct data once, but then it follow with garbage data.
void TextFileLogger::log(std::string msg){
using namespace std;
//ofstream output_file("students.data", ios::binary);
std::ofstream logFile;
// creating, opening and writing/appending data to a file
char filename[] = "log.txt";
logFile.open(filename, ios::binary | ios::app |ios::out);
if (logFile.fail())
{
std::cout << "The " << filename << " file could not be created/opened!" << std::endl;
// 0-normal, non zero - some errors
}
else
{
if (!logFile.write((char*)&msg, sizeof(msg)))
{
cout << "Could not write file" << endl;
}
else
{
streamsize bytesWritten = logFile.tellp();
if (bytesWritten != sizeof(msg))
{
cout << "Could not write expected number of bytes" << endl;
}
else
{
logFile << msg << std::endl;
cout << "file written OK" << endl;
}
}
}
}
That one is fun!
(char*)&msg does not do what you expect: std::string is mainly a pointer to a dynamically-allocated buffer which contains the actual data. When you take the std::string's address and try to read what's inside, you get a view of its innards, not its data. Using a C++ static_cast here would have spared you the trouble by telling you that the conversion makes no sense. sizeof(msg) similarly returns the size of the std::string, not the length of its data.
So, your solution is: use msg.data() and msg.size(), it's exactly what they're designed for.
But... why would it (sometimes) output your string, and a bunch of garbage? Well, std::strings typically use SSO (Small String Optimization). The std::string actually contains a small buffer, to store short enough strings without dynamic allocation. When you inspect the whole std::string object, you see this buffer pass by.
You are writing the contents of the whole std::string object, with all the member variables that it contains internally.
You either want:
logFile << msg;
or if you really want to use write():
logFile.write( msg.c_str(), msg.length());
And, I wonder: Why do create/open the file in binary mode, when you write strings afterwards?
And finally, you write the data twice, the second time in your last else clause.
The problem is with this line:
if (!logFile.write((char*)&msg, sizeof(msg)))
It should be this:
if (!logFile.write(msg.c_str(), msg.length()))
Since you are passing a std::string into the function, you should take advantage of the functions it provides (c_str() and length()) instead of trying to cast it to a char* (this always gets messy, plus you are casting away the const, which is also typically bad).
This:
if (!logFile.write((char*)&msg, sizeof(msg)))
is wrong in so many ways. msg is not an array of char, it's a std::string - lying to the compiler by using a cast is always a bad thing to do. And the size of a string is not the size of the characters it contains. Why the heck are you not using the obvios:
logfile << msg << std::endl;
Replace sizeof(msg) with msg.size(), sizeof() is not doing what you think!
Also (char*)&msg does not do whatever you think, use msg.data() instead.
logFile.write((char*)&msg, sizeof(msg));
should be rewritten to:
logFile.write(msg.data(), msg.size());
or, even better, because ofstream overrides operator<< for std::string:
logfile << msg;

Insert values into a string without using sprintf or to_string

Currently I only know of two methods to insert values into a C++ string or C string.
The first method I know of is to use std::sprintf() and a C-string buffer (char array).
The second method is to use something like "value of i: " + to_string(value) + "\n".
However, the first one needs the creation of a buffer, which leads to more code if you just want to pass a string to a function. The second one produces long lines of code, where a string gets interrupted every time a value is inserted, which makes the code harder to read.
From Python I know the format() function, which is used like this:
"Value of i: {}\n".format(i)
The braces are replaced by the value in format, and further .format()'s can be appended.
I really like Python's approach on this, because the string stays readable, and no extra buffer needs to be created. Is there any similar way of doing this in C++?
Idiomatic way of formatting data in C++ is with output streams (std::ostream reference). If you want the formatted output to end up in a std::string, use an output string stream:
ostringstream res;
res << "Value of i: " << i << "\n";
Use str() member function to harvest the resultant string:
std::string s = res.str();
This matches the approach of formatting data for output:
cout << "Value of i: " << i << "\n";

C++ and Python- checking type of PyObject fails

I'm a bit confused. I'm trying to do some C++ and Python integration, but it's less than straightforward. I'm not using Boost, because I couldn't get Boost::Python to compile properly. But that's another story.
Currently, here's what I'm doing in C++:
//set everything up
PyObject* py_main_module = PyImport_AddModule("__main__");
PyObject* py_global_dict = PyModule_GetDict(py_main_module);
PyObject* py_local_dict = PyDict_New();
PyObject* py_return_value;
PyRun_SimpleString(data.c_str()); //runs Python code, which defines functions
//call a function defined by the python code
py_return_value = PyRun_String("test()", Py_single_input, py_global_dict, py_local_dict);
//attempt to check the type of the returned value
if(py_return_value != NULL) {
//this is the problem: all of these print 0
cout << PyList_Check(py_return_value) << endl;
cout << PySet_Check(py_return_value) << endl;
cout << PyFloat_Check(py_return_value) << endl;
} else {
cout << "IT WAS NULL?!" << endl;
}
The Python program (input to the C++ program as the string named "data"):
def test():
derp = 1.234
#derp = [1, 2, 3, 4]
#derp = set([1, 2, 3, 4])
return derp
Now, the problem is that the type checks aren't working. They all return 0, regardless of whether the Python function is returning a float, a list, or a set. What am I doing wrong?
Bonus points if anyone can tell me why the call to PyRun_String prints the returned value in the console. It's really annoying.
From the docs:
int Py_eval_input
The start symbol from the Python grammar for isolated expressions; for use with Py_CompileString().
int Py_file_input
The start symbol from the Python grammar for sequences of statements as read from a file or other source; for use with
Py_CompileString(). This is the symbol to use when compiling
arbitrarily long Python source code.
int Py_single_input
The start symbol from the Python grammar for a single statement; for use with Py_CompileString(). This is the symbol used for the
interactive interpreter loop.
Py_single_input evaluates the string as a statement. Statements don't inherently return anything, so you'll get None back from PyRun_String. Use Py_eval_input instead to evaluate the string as an expression and get a result.
Changing Py_single_input to Py_eval_input seems to resolve both issues.
The former treats the string as part of the interpreter loop, while the latter evaluates a single expression and gives you an object back. (I'm not sure what the return value means in the former case, but it's not the value of the expression.)
EDIT: Just tested it, and as per nneonneo's answer below, the result with Py_single_input is indeed Py_None.

Access from map, casting string to char*

I have the following problem with casting (or accessing data, I'm not sure):
There is map<int, string> keys I use to describe buttons, number matches the Allegro5 key code (eg. keys[81] = "PgDown"). I want to print data on the screen using al_draw_text, which needs char* parameter to be passed.
I tried access data this way:
char dropdownBuffer[16];
cout << keys[dropdownKeyCode] << endl;
sprintf_s(dropdownBuffer, "%s", keys[dropdownKeyCode]);
cout << dropdownBuffer << endl;
gui.drawButton(CLIENT_PADDING, CLIENT_PADDING+50+219, 123, 38, dropdownBuffer);
Console should print string Spacja twice, but second time there are some random chars.
What am I doing wrong now? Maybe there is other possible way to cast this string to char?
to get pointer to data from string, use c_str(), see ref: this
replace code:
sprintf_s(dropdownBuffer, "%s", keys[dropdownKeyCode]);
with:
sprintf_s(dropdownBuffer, "%s", keys[dropdownKeyCode].c_str());
I thought sprintf_s needs a sizeof argument.
sprintf_s(dropdownBuffer, sizeof(dropdownBuffer), "%s", keys[dropdownKeyCode]);
or
const char* dropdownBuffer = keys[dropdownKeyCode].c_str();

Reading a string from a file in C++

I'm trying to store strings directly into a file to be read later in C++ (basically for the full scope I'm trying to store an object array with string variables in a file, and those string variables will be read through something like object[0].string). However, everytime I try to read the string variables the system gives me a jumbled up error. The following codes are a basic part of what I'm trying.
#include <iostream>
#include <fstream>
using namespace std;
/*
//this is run first to create the file and store the string
int main(){
string reed;
reed = "sees";
ofstream ofs("filrsee.txt", ios::out|ios::binary);
ofs.write(reinterpret_cast<char*>(&reed), sizeof(reed));
ofs.close();
}*/
//this is run after that to open the file and read the string
int main(){
string ghhh;
ifstream ifs("filrsee.txt", ios::in|ios::binary);
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
cout<<ghhh;
ifs.close();
return 0;
}
The second part is where things go haywire when I try to read it.
Sorry if it's been asked before, I've taken a look around for similar questions but most of them are a bit different from what I'm trying to do or I don't really understand what they're trying to do (still quite new to this).
What am I doing wrong?
You are reading from a file and trying to put the data in the string structure itself, overwriting it, which is plain wrong.
As it can be verified at http://www.cplusplus.com/reference/iostream/istream/read/ , the types you used were wrong, and you know it because you had to force the std::string into a char * using a reinterpret_cast.
C++ Hint: using a reinterpret_cast in C++ is (almost) always a sign you did something wrong.
Why is it so complicated to read a file?
A long time ago, reading a file was easy. In some Basic-like language, you used the function LOAD, and voilĂ !, you had your file.
So why can't we do it now?
Because you don't know what's in a file.
It could be a string.
It could be a serialized array of structs with raw data dumped from memory.
It could even be a live stream, that is, a file which is appended continuously (a log file, the stdin, whatever).
You could want to read the data word by word
... or line by line...
Or the file is so large it doesn't fit in a string, so you want to read it by parts.
etc..
The more generic solution is to read the file (thus, in C++, a fstream), byte per byte using the function get (see http://www.cplusplus.com/reference/iostream/istream/get/), and do yourself the operation to transform it into the type you expect, and stopping at EOF.
The std::isteam interface have all the functions you need to read the file in different ways (see http://www.cplusplus.com/reference/iostream/istream/), and even then, there is an additional non-member function for the std::string to read a file until a delimiter is found (usually "\n", but it could be anything, see http://www.cplusplus.com/reference/string/getline/)
But I want a "load" function for a std::string!!!
Ok, I get it.
We assume that what you put in the file is the content of a std::string, but keeping it compatible with a C-style string, that is, the \0 character marks the end of the string (if not, we would need to load the file until reaching the EOF).
And we assume you want the whole file content fully loaded once the function loadFile returns.
So, here's the loadFile function:
#include <iostream>
#include <fstream>
#include <string>
bool loadFile(const std::string & p_name, std::string & p_content)
{
// We create the file object, saying I want to read it
std::fstream file(p_name.c_str(), std::fstream::in) ;
// We verify if the file was successfully opened
if(file.is_open())
{
// We use the standard getline function to read the file into
// a std::string, stoping only at "\0"
std::getline(file, p_content, '\0') ;
// We return the success of the operation
return ! file.bad() ;
}
// The file was not successfully opened, so returning false
return false ;
}
If you are using a C++11 enabled compiler, you can add this overloaded function, which will cost you nothing (while in C++03, baring optimizations, it could have cost you a temporary object):
std::string loadFile(const std::string & p_name)
{
std::string content ;
loadFile(p_name, content) ;
return content ;
}
Now, for completeness' sake, I wrote the corresponding saveFile function:
bool saveFile(const std::string & p_name, const std::string & p_content)
{
std::fstream file(p_name.c_str(), std::fstream::out) ;
if(file.is_open())
{
file.write(p_content.c_str(), p_content.length()) ;
return ! file.bad() ;
}
return false ;
}
And here, the "main" I used to test those functions:
int main()
{
const std::string name(".//myFile.txt") ;
const std::string content("AAA BBB CCC\nDDD EEE FFF\n\n") ;
{
const bool success = saveFile(name, content) ;
std::cout << "saveFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n" ;
}
{
std::string myContent ;
const bool success = loadFile(name, myContent) ;
std::cout << "loadFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n"
<< "content is: [" << myContent << "]\n"
<< "content ok is: " << (myContent == content)<< "\n" ;
}
}
More?
If you want to do more than that, then you will need to explore the C++ IOStreams library API, at http://www.cplusplus.com/reference/iostream/
You can't use std::istream::read() to read into a std::string object. What you could do is to determine the size of the file, create a string of suitable size, and read the data into the string's character array:
std::string str;
std::ifstream file("whatever");
std::string::size_type size = determine_size_of(file);
str.resize(size);
file.read(&str[0], size);
The tricky bit is determining the size the string should have. Given that the character sequence may get translated while reading, e.g., because line end sequences are transformed, this pretty much amounts to reading the string in the general case. Thus, I would recommend against doing it this way. Instead, I would read the string using something like this:
std::string str;
std::ifstream file("whatever");
if (std::getline(file, str, '\0')) {
...
}
This works OK for text strings and is about as fast as it gets on most systems. If the file can contain null characters, e.g., because it contains binary data, this doesn't quite work. If this is the case, I'd use an intermediate std::ostringstream:
std::ostringstream out;
std::ifstream file("whatever");
out << file.rdbuf();
std::string str = out.str();
A string object is not a mere char array, the line
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
is probably the root of your problems.
try applying the following changes:
char[BUFF_LEN] ghhh;
....
ifs.read(ghhh, BUFF_LEN);