Function to convert logic packed arrays of arbitrary value to string - casting

I am trying to write a function that takes as input a logic[:] value of arbitrary length to a string. But, I am unable to find a correct way of implementing it.
I have tried the following approach:
function string bin_to_string (input logic _bin_ [] );
automatic string _str_;
_str_ = "";
for(int i=0; i< _bin_.size(); i++) begin
automatic string tmpstr;
tmpstr.bintoa(_bin_[i]);
_str_ = {_str_, tmpstr };
end
return _str_;
endfunction : bin_to_string
Which raises the following error:
Arg. 'bin' of 'bin_to_string': Cannot assign a packed type 'reg[3:0]' to an unpacked type 'reg $[]'.
Is creating a generic cast function for arbitrary length registers possible?

The error message you show is from calling the function, not its implementation. You need to stream the packed array argument to an unpacked array.
mystring = bin_to_string( {>>{ input_arg }} );
Also, you have a typo in that size is never defined. But it would be easier to use foreach( _bin_[i] ) instead

Related

LLVM Pass - Issues replacing a GlobalVariable

I am trying to write an LLVM pass which manipulates strings.
After iterating all the GlobalVariable objects and picking out the strings, I get the string data, perform the manipulation, create a new GlobalVariable and then use replaceAllUsesWith() to replace the old with the new. Sounds simple enough...
However, I am getting an assert error, telling me that the replacement should be the same type. I have not changed the length of the string, so I don't know why the type would be different. A cut down version of the code is below.
for (Module::global_iterator gi = M.global_begin(), ge = M.global_end(); gi != ge; gi++) {
GlobalVariable *gv = *gi;
ConstantDataSequential *cdata = dyn_cast<ConstantDataSequential>(gv->getInitializer());
std::string orig = "";
if (cdata->isString() {
orig = cdata->getAsString();
} else if (cdata->isCString() {
orig = cdata->getAsCString();
} else {
continue;
}
// string returned has the same length, but different contents
std::string modified = manipulateString(orig);
std::ostringstream oss;
oss << gv->getName() << "Modified" ;
Constant *cMod = ConstantDataArray::getString(M.getContext(), modified, true);
GlobalVariable *newGv = new GlobalVariable(M,
cMod->getType(),
true,
GlobalValue::ExternalLinkage,
cMod,
oss.str());
gv->replaceAllUsesWith(newGv);
}
Note: I've hand typed this code, so it may not compile, but it should serve as an illustration of what I'm trying to achieve and how I'm trying to achieve it.
For some reason, the new GlobalVariable has a different type. Printing the types at runtime yields:
gv->getType() = [36 x i8]*
newGv->getType() = [37 * x i8]*
The size of both strings are 36 chars. Why is the type of the new GlobalVariable different, even though the string length has not changed? Why has an extra element been added?
Also, replaceAllUsesWith() requires that the replacement be same type. If I wanted the replacement to be string of a different length, how would I achieve that?
You cannot replace with an object of a different type. You can, however, cast the GlobalVariable to have the right type. What you want is...
ConstantExpr::getPointerCast(newGv, gv->getType());
...except that that won't compile, because the second argument has to be a PointerType. You can always add another level of casting, making the code less clear but the compiler more happy:
ConstantExpr::getPointerCast(newGv, cast<PointerType>(gv->getType()));
I have found it helpful to user 0-length arrays for all variable-length arrays, and always cast constants to that.

llvm, defining strings and arrays via c++ API

I develop a toy compiler, and trying to implement strings and arrays.
I have noticed that clang creates always a global variable for those types, even if they where defined within a function.
I guess that there is a good reason for that, so I try to do the same.
My problem is that I cannot figure out how to do it via c++ API.
kalidoscope tutorial does not cover strings and arrays, so the only source that I have found is the documentation.
In the documentation for the Module class, there is the function getOrInsertGlobal, which looks relevant, but I cannot understand how I set the actual value of the global. The function arguments include only the name and the type of the variable. So where does the value go?
So the question is: how can I define a global string, such as "hello" or array, such as [i32 1, i32 2] in llvm c++ API? Any example would be really appreciated.
What you want is called a read-only GlobalVariable and you need that variable, an initializer, and probably a constant cast so that all of your strings can have the same type.
Suppose your strings are the C kind — null-terminated sequences of bytes. In that case you'll want your strings to be an array of zero bytes, so that all arrays have the same type. But the initialisers need to be arrays of the right numbers of bytes, so that each initialiser's type will match its value. So you create your array using something like this (cut and pasted together from bits of code I've written, won't even compile, is not the most efficient way, but does contain most of the building blocks you need):
std::vector<llvm::Constant *> chars(utf8string.size());
for(unsigned int i = 0; i < utf8string.size(); i++)
chars[i] = ConstantInt::get(i8, utf8string[i]);
auto init = ConstantArray::get(ArrayType::get(i8, chars.size()),
entries);
GlobalVariable * v =
new GlobalVariable(module, init->getType(), true,
GlobalVariable::ExternalLinkage, init,
utf8string);
return ConstantExpr::getBitCast(v, i8->getPointerTo());
Note that a GlobalVariable is a pointer to whatever it's been initialised as, so if you initialise it with the five-byte sequence "test\0", then it'll be a pointer to a five bytes. Or, if you cast, it can be a pointer to 0 bytes (LLVM lets you index past the official end), or it can be an instance if an abstract type you define.
Using the code and the help of #arnt on the answer above, and I ended up with the following code to implement a string initialization. It now works, and also avoids the call to new, so it does not require any cleanup later.
I post it, hoping that it may be useful for someone.
llvm::Value* EulStringToken::generateValue(llvm::Module* module, llvm::LLVMContext context) {
//0. Defs
auto str = this->value;
auto charType = llvm::IntegerType::get(context, 8);
//1. Initialize chars vector
std::vector<llvm::Constant *> chars(str.length());
for(unsigned int i = 0; i < str.size(); i++) {
chars[i] = llvm::ConstantInt::get(charType, str[i]);
}
//1b. add a zero terminator too
chars.push_back(llvm::ConstantInt::get(charType, 0));
//2. Initialize the string from the characters
auto stringType = llvm::ArrayType::get(charType, chars.size());
//3. Create the declaration statement
auto globalDeclaration = (llvm::GlobalVariable*) module->getOrInsertGlobal(".str", stringType);
globalDeclaration->setInitializer(llvm::ConstantArray::get(stringType, chars));
globalDeclaration->setConstant(true);
globalDeclaration->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage);
globalDeclaration->setUnnamedAddr (llvm::GlobalValue::UnnamedAddr::Global);
//4. Return a cast to an i8*
return llvm::ConstantExpr::getBitCast(globalDeclaration, charType->getPointerTo());
}

How can I insert integer into a string using insert function in C++?

Below check is string and temp1->data is integer. I want to insert temp1->data into check. So I type cast int into const char*. This gives warning : cast to pointer from integer of different size [-Wint-to-pointer-cast]
Part of code:
temp1 = head;
std::string check;
check = "";
int i = 0;
while(temp1 != NULL)
{
check.insert(i, (const char*)temp1->data);// here is the warning
temp1 = temp1->next;
++i;
}
I want to know what other choices I have to insert the integer (temp1->data) into string(check) using insert function and what is the actual effect of warning [-Wint-to-pointer-cast] on my code.
Points:
data is integer, next is pointer to Node
I'm trying to implement a function to check if a linked list containing single digit number is palindrome or not. Yes, I know other methods for this but I just want to implement through this method too.
Here I want to store all the data of linked list into a string and directly check if the string is palindrome or not.
This question may seem duplicate of this . But it is not, here I explicitly asked for inserting integer into string using insert function contained in string class.
PS: on using std::to_string(temp1->data) gives me error ‘to_string’ is not a member of ‘std’.
You can use std::to_string function to convert integer to string and then insert it in a string using insert function on std::string.
std::string check;
check = "";
int i = 0;
check.insert(i, std::to_string(10));
The reason you are getting error "to_string is not a member of std" is may be because you did not include <string> header.
First, here's a way to convert an integer to a string without much work. You basically create a stream, flush the int into it, and then extract the value you need. The underlying code will handle the dirty work.
Here's a quick example:
stringstream temp_stream;
int int_to_convert = 5;
temp_stream << int_to_convert;
string int_as_string(temp_stream.str());
Here's more info on this solution and alternatives if you want to know more:
Easiest way to convert int to string in C++
Regarding the impact of the cast that you're doing, the behavior will be undefined because you're setting char* to an int value. The effect won't be converting the int value to a series of characters, instead you'll be setting the memory location of what the system interprets as the location of first character of a char array to the value of the int.

dynamically allocated string array

When I attempt to run this code it crashes. There are no error messages. When the program compiles and runs, it just displays the windows 7 message, "this program has stopped working.":
void readGameFile(string ** entries, int * num_entries, string ** story, int * num_lines)
{
ifstream madlib("madlibs1.txt");
string line;
getline(madlib, line);
*num_entries=stoi(line);
*entries=new string [*num_entries];
for (int i=0; i<*num_entries; i++)
{
getline(madlib,*entries[i]);
}
I did a few tests, and it seems to assign entries[0] a value, and then crashes when attempting to assign entries[1] a value. I am forced to use this function name, with those function parameters and parameter types specifically. I also may not use malloc, vector or other answers I've seen.
I think the issue is one of precedence: you almost certainly
want:
getline( madlib, (*entries)[i]) );
Otherwise, you're indexing from the string**, then
dereferencing: *(entries[i]).
You also want to check the results of getline, possibly in the
loop:
for ( int i = 0; madlib && i != *num_entries; ++ i )...
as well as before the std::stoi.
And finally: I don't know why you are forced to use this
function signature. It is horrible C++, and you should never
write anything like this. Logically, std::vector<string>
would be a better solution, but even without it: your function
has 4 out parameters. This would be better handled by returning
a struct. And failing that, out parameters in C++ are
usually implemented by non-const reference, not by a pointer.
While there are arguments for using the pointer in some cases,
when it results in a pointer to a pointer, it's evil. If
nothing else:
bool // Because we have to indicate whether it succeed or failed
readGameFile( std::string* &entries, int &num_entries, std::string* &story, int &num_lines )
// ...
(This actually looks more like it should be constructor,
however, of a class with two data elements, entries and
story.)

Strings to binary files

My problem goes like this: I have a class called 'Register'. It has a string attribute called 'trainName' and its setter:
class Register {
private:
string trainName;
public:
string getTrainName();
};
As a matter of fact, it is longer but I want to make this simpler.
In other class, I copy several Register objects into a binary file, previously setting trainName.
Register auxRegister = Register();
auxRegister.setName("name");
for(int i = 0; i < 10; i++) {
file.write(reinterpret_cast<char*>(&auxRegister),sizeof(Register));
}
Later on, I try to retrieve the register from the binary file:
Register auxRegister = Register();
while(!file.eof()) { //I kwnow this is not right. Which is the right way?
file.read(reinterpret_cast<char*>(&auxRegister), sizeof(Register));
}
It occurs it does not work. Register does, in fact, have more attributes (they are int) and I retrieve them OK, but it's not the case with the string.
Am I doing something wrong? Should I take something into consideration when working with binary files and strings?
Thank you very much.
The std::string class contains a pointer to a buffer where the string is stored (along with other member variables). The string buffer itself is not a part of the class. So writing out the contents of an instance of the class is not going to work, since the string will never be part of what you dump into the file, if you do it that way. You need to get a pointer to the string and write that.
Register auxRegister = Register();
auxRegister.setName("name");
auto length = auxRegister.size();
for(int i = 0; i < 10; i++) {
file.write( auxRegister.c_str(), length );
// You'll need to multiply length by sizeof(CharType) if you
// use a wstring instead of string
}
Later on, to read the string, you'll have to keep track of the number of bytes that were written to the file; or maybe fetch that information from the file itself, depending on the file format.
std::unique_ptr<char[]> buffer( new char[length + 1] );
file.read( buffer, length );
buffer[length] = '\0'; // NULL terminate the string
Register auxRegister = Register();
auxRegister.setName( buffer );
You cannot write string this way, as it almost certainly contains pointers to some structs and other binary stuff that cannot be serialized at all.
You need to write your own serializing function, and write the string length + bytes (for example) or use complete library, for example, protobuf, which can solve serializing problem for you.
edit: see praetorian's answer. much better than mine (even with lower score at time of this edit).