My goal is to run a function in Node.js and provide a string as parameter/argument:
const myAddon = require('path'); myAddon.myFunc(otherData, "Hello World");
What I've tried, according to a Stack Overflow answer, was the following:
v8::String::Utf8Value STR_ARG(args[1]->ToString());
std::string USER_MESSAGE = std::string(*STR_ARG);
This didn't work and I got this error Message:
ToString Function doesn't accept 0 Arguments.
Am I supposed to give the function any input and if so, what?
The Solution I tried to approach in my Question didnt work properly. If it doesnt work for you either, here is the Code that Worked for me.
v8::Local<v8::String> argsINPUT;
argsINPUT = args[0].As<v8::String>();
//Having a Second Argument to determine the Buffer Size is Optional but Optimal
//new char[insert any int] is also okay, but it should be large enough to hold the Data
char* args_buffer = new char[args[1].As<Number>()->Value()];
//If the Next Line thows an Error try out '(argsINPUT*)' instead of 'argsINPUT'
argsINPUT->WriteUtf8(isolate, args_buffer);
static std::string finalString;
finalString.assign(args_buffer);
delete args_buffer;
args.GetReturnValue().Set(String::NewFromUtf8(
isolate, finalString.c_str()).ToLocalChecked());
Credits to Cybafelo:
Convert std::string to v8::string and viceversa?
And Botje for helping me out with my Original Approach
Edit: Reason why args[1].As<Number>()->Value() might be optimal as Buffer Size:
js code example:
const myAddon = require('path');
const myString = "Hello World";
myAddon.myFunc(myString, myString.length + 1);
As you can see and probably know, Js provides us with properties and functions, one of them is the length Property, so we can just Directly create a perfectly size Buffer. The +1 is at the end because Im not sure if we need also space for "\0"
Related
This is an old problem, which I have observed in past. So thought of getting a clarification once & for all. There are many standard / orthodox C library functions, which deal only with C-style strings. For example, my current implementation looks like this:
std::string TimeStamp (const time_t seconds) // version-1
{
auto tm = *std::localtime(&seconds); // <ctime>
char readable[30] = {};
std::strftime(&readable[0], sizeof(readable) - 1, "%Y-%h-%d %H:%M:%S:", &tm);
return readable;
}
Above works as expected. But as you can see, that the readable is copied from stack array to std::string. Now this function is called very frequently for logging & other purposes.
Hence, I converted it to following:
std::string TimeStamp (const time_t seconds) // version-2
{
auto tm = *std::localtime(&seconds); // <ctime>
std::string readable(30,0);
std::strftime(&readable[0], readable.length(), "%Y-%h-%d %H:%M:%S:", &tm);
return readable;
}
At unit test level, it apparently seems to work. But for overall logging in my much larger code, it somehow gets messed up. A new line character appears after this output & many of the output strings which are called outside this function are not printed. Such issue happens only when the "version-1" is changed to "version-2".
Even following modification also doesn't help:
readable.resize(1 + std::strftime(&readable[0], readable.length(), "%Y-%h-%d %H:%M:%S:", &tm));
Is there anything wrong in my code? What is the correct way of directly using std::string in the C-style string functions?
Your first function is correct. There is no point mucking around with the troublesome details in the second function because even once you get it right, it is no improvement on the first function.
In fact it might even perform worse, because of the need to over-allocate the string and resize it down. For example perhaps the size 30 exceeds the size for Small String Optimization but the actual length of data doesn't.
std::string can have \0 in it.
so
std::string s1 = "ab\0\0cd"; // s1 contains "ab" -> size = 2
std::string s2{"ab\0\0cd", 6}; // s2 contains "ab\0\0cd" -> size = 6
Your first snippet use constructor 1 whereas the second is similar to the second one (string of size 30 filled with \0).
So you have to resize correctly your string to avoid trailling \0.
I have found myself writing code which looks like this
// Treat the following as pseudocode - just an example
iofile.seekg(0, std::ios::end); // iofile is a file opened for read/write
uint64_t f_len = iofile.tellg();
if(f_len >= some_min_length)
{
// Focus on the following code here
char *buf = new char[7];
char buf2[]{"MYFILET"}; // just some random string
// if we see this it's a good indication
// the rest of the file will be in the
// expected format (unlikely to see this
// sequence in a "random file", but don't
// worry too much about this)
iofile.read(buf, 7);
if(memcmp(buf, buf2, 7) == 0) // I am confident this works
{
// carry on processing file ...
// ...
// ...
}
}
else
cout << "invalid file format" << endl;
This code is probably an okay sketch of what we might want to do when opening a file, which has some specified format (which I've dictated). We do some initial check to make sure the string "MYFILET" is at the start of the file - because I've decided all my files for the job I'm doing are going to start with this sequence of characters.
I think this code would be better if we didn't have to play around with "c-style" character arrays, but used strings everywhere instead. This would be advantageous because we could do things like if(buf == buf2) if buf and buf2 where std::strings.
A possible alternative could be,
// Focus on the following code here
std::string buf;
std::string buf2("MYFILET"); // very nice
buf.resize(7); // okay, but not great
iofile.read(buf.data(), 7); // pretty awful - error prone if wrong length argument given
// also we have to resize buf to 7 in the previous step
// lots of potential for mistakes here,
// and the length was used twice which is never good
if(buf == buf2) then do something
What are the problems with this?
We had to use the length variable 7 (or constant in this case) twice. Which is somewhere between "not ideal" and "potentially error prone".
We had to access the contents of buf using .data() which I shall assume here is implemented to return a raw pointer of some sort. I don't personally mind this too much, but others may prefer a more memory-safe solution, perhaps hinting we should use an iterator of some sort? I think in Visual Studio (for Windows users which I am not) then this may return an iterator anyway, which will give [?] warnings/errors [?] - not sure on this.
We had to have an additional resize statement for buf. It would be better if the size of buf could be automatically set somehow.
It is undefined behavior to write into the const char* returned by std::string::data(). However, you are free to use std::vector::data() in this way.
If you want to use std::string, and dislike setting the size yourself, you may consider whether you can use std::getline(). This is the free function, not std::istream::getline(). The std::string version will read up to a specified delimiter, so if you have a text format you can tell it to read until '\0' or some other character which will never occur, and it will automatically resize the given string to hold the contents.
If your file is binary in nature, rather than text, I think most people would find std::vector<char> to be a more natural fit than std::string anyway.
We had to use the length variable 7 (or constant in this case) twice.
Which is somewhere between "not ideal" and "potentially error prone".
The second time you can use buf.size()
iofile.read(buf.data(), buf.size());
We had to access the contents of buf using .data() which I shall
assume here is implemented to return a raw pointer of some sort.
And pointed by John Zwinck, .data() return a pointer to const.
I suppose you could define buf as std::vector<char>; for vector (if I'm not wrong) .data() return a pointer to char (in this case), not to const char.
size() and resize() are working in the same way.
We had to have an additional resize statement for buf. It would be
better if the size of buf could be automatically set somehow.
I don't think read() permit this.
p.s.: sorry for my bad English.
We can validate a signature without double buffering (rdbuf and a string) and allocating from the heap...
// terminating null not included
constexpr char sig[] = { 'M', 'Y', 'F', 'I', 'L', 'E', 'T' };
auto ok = all_of(begin(sig), end(sig), [&fs](char c) { return fs.get() == (int)c; });
if (ok) {}
template<class Src>
std::string read_string( Src& src, std::size_t count){
std::string buf;
buf.resize(count);
src.read(&buf.front(), 7); // in C++17 make it buf.data()
return buf;
}
Now auto read = read_string( iofile, 7 ); is clean at point of use.
buf2 is a bad plan. I'd do:
if(read=="MYFILET")
directly, or use a const char myfile_magic[] = "MYFILET";.
I liked many of the ideas from the examples above, however I wasn't completely satisfied that there was an answer which would produce undefined-behaviour-free code for C++11 and C++17. I currently write most of my code in C++11 - because I don't anticipate using it on a machine in the future which doesn't have a C++11 compiler.
If one doesn't, then I add a new compiler or change machines.
However it does seem to me to be a bad idea to write code which I know may not work under C++17... That's just my personal opinion. I don't anticipate using this code again, but I don't want to create a potential problem for myself in the future.
Therefore I have come up with the following code. I hope other users will give feedback to help improve this. (For example there is no error checking yet.)
std::string
fstream_read_string(std::fstream& src, std::size_t n)
{
char *const buffer = new char[n + 1];
src.read(buffer, n);
buffer[n] = '\0';
std::string ret(buffer);
delete [] buffer;
return ret;
}
This seems like a basic, probably fool-proof method... It's a shame there seems to be no way to get std::string to use the same memory as allocated by the call to new.
Note we had to add an extra trailing null character in the C-style string, which is sliced off in the C++-style std::string.
I am working on Visual studio C++.
I have these codes:
CString str;
BYTE byBuffer[10000] = { 0 };
str ="Invalid Command. Spaces are not allowed too!!";
strcpy_s(reinterpret_cast<LPSTR>(byBuffer), 10000, T2CA(str ));
The problem is byBuffer = "Invalid Command. Spaces are not allowed too!!"; but after the following line, the string changes.
LPBYTE lp=byBuffer ; Although it works fine for small strings like OK, GOOD JOB. etc..
i am debugging the whole code by setting the breakpoints. moreover this function has been called to another function in which ( LPBYTE lpBuffer) recieved this value.
Plz help
The code you're showing us looks OK, so I'm going out on a limb and making a guess.
I'm guessing that you're trying to return this buffer from a function:
LPBYTE lp = byBuffer;
return lp;
If that's the case, then the local variable byBuffer is getting destroyed at the end of the function and the pointer no longer points to valid memory. You're lucky if you can see anything recognizable in the output at all.
Basically I'm reading the contents of a file using fstream then converting it to const char* type. I'm supplying this to Lua, and Lua will do something with this. This however does not work. What does work is if I do:
const char* data = "print('Hello world')";
luaL_pushstring(L, data);
luaL_setglobal(L, "z");
They both are in the type const char* type and they are both the same string (e.g. I compared the two lengths). Except one works, and the other. I'm baffled. Any help here? Here is the code:
std::string line,text;
std::ifstream in("test.txt");
while(std::getline(in, line))
{
text += line;
}
const char* data = text.c_str();
luaL_pushstring(L, data);
luaL_setglobal(L, "z");
Here is the Lua code:
loadstring(z)()
To diagnose this, you probably want to know more about what Lua thought. I'd write the Lua side as assert(loadstring(s))() instead. If loadstring fails, your current code at best prints an error from the attempt to call nil. With the assert() in the sequence, the call to nil will be replaced by a more informative error about what went wrong.
Don't you have to set the global before you push the value? Anyways, what's up, Camoy :P
How to convert AS3 ByteArray into wchar_t const* filename?
So in my C code I have a function waiting for a file with void fun (wchar_t const* filename) how to send to that function my ByteArray? (Or, how should I re-write my function?)
A four month old question. Better late than never?
To convert a ByteArray to a String in AS3, there are two ways depending on how the String was stored. Firstly, if you use writeUTF it will write an unsigned short representing the String's length first, then write out the string data. The string is easiest to recover this way. Here's how it's done in AS3:
byteArray.position = 0;
var str:String = byteArray.readUTF();
Alternatively, toString also works in this case. The second way to store is with writeUTFBytes. This won't write the length to the beginning, so you'll need to track it independantly somehow. It sounds like you want the entire ByteArray to be a single String, so you can use the ByteArray's length.
byteArray.position = 0;
var str:String = byteArray.readUTFBytes(byteArray.length);
Since you want to do this with Alchemy, you just need to convert the above code. Here's a conversion of the second example:
std::string batostr(AS3_Val byteArray) {
AS3_SetS(byteArray, "position", AS3_Int(0));
return AS3_StringValue(AS3_CallS("readUTFBytes", byteArray,
AS3_Array("AS3ValType", AS3_GetS(byteArray, "length")) ));
}
This has a ridiculous amount of memory leaks, of course, since I'm not calling AS3_Release anywhere. I use a RAII wrapper for AS3_Val in my own code... for the sake of my sanity. As should you.
Anyway, the std::string my function returns will be UTF-8 multibyte. Any standard C++ technique for converting to wide characters should work from here. (search the site for endless reposts) I suggest leaving it is as it, though. I can't think of any advantage to using wide characters on this platform.