digitalmars.D.learn - printf, writeln, writefln
- johannes (20/20) Dec 06 2022 //-- the result should be f.i. "the sun is shining"
- H. S. Teoh (7/11) Dec 06 2022 [...]
- ryuukk_ (33/44) Dec 06 2022 no, you don't "need" to use std conv
- ag0aep6g (8/19) Dec 06 2022 [...]
- Salih Dincer (27/37) Dec 08 2022 Well, if we think the other way around, in the example below we'd
- =?UTF-8?Q?Ali_=c3=87ehreli?= (8/11) Dec 08 2022 Nothing disappeared on my system. (?)
- Salih Dincer (10/19) Dec 08 2022 You're right, it's my fault. Copy commands are always used as
- Nick Treleaven (6/17) Dec 10 2022 Yes, with function call syntax it's more understandable if an
- Salih Dincer (34/37) Dec 18 2022 Hi Nick, do you think this is confusing?
- Salih Dincer (27/37) Dec 08 2022 Well, if we think the other way around, in the example below we'd
- Siarhei Siamashka (73/93) Dec 06 2022 What you are posting here is a perfect example of unsafe code.
- ryuukk_ (7/104) Dec 06 2022 That's a good point, but there also no error checking for the
- =?UTF-8?Q?Ali_=c3=87ehreli?= (17/24) Dec 07 2022 printf behaves as what you expect because %s means dereferencing the
- johannes (1/1) Dec 08 2022 Thank you all for those explanations. Helps a lot!
//-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i)); writefln("%s",sqlite3_column_text(res, i)); writefln(std.conv.to!string(sqlite3_column_text(res, i))); //-- the result : the sun is shining 55B504B3CE98 55B504B3CE98 the sun is shining => without 'std.conv.to!string' I presume 'write' prints out the address of the first byte. This is odd to me because printf does the job correctly. But I think to understand that write in D interpretes char* as a pointer to a byte. So it prints the address that the pointer points to.(?) So the question : for every c funtion returning char* I will have to use std.conv.to!string(..) or is there another way. Also it seems the formatting "%s" has another meaning in D ?
Dec 06 2022
On Tue, Dec 06, 2022 at 11:07:32PM +0000, johannes via Digitalmars-d-learn wrote://-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i));[...] In D, strings are not the same as char*. You should use std.conv.fromStringZ to convert the C char* to a D string. T -- For every argument for something, there is always an equal and opposite argument against it. Debates don't give answers, only wounded or inflated egos.
Dec 06 2022
On Tuesday, 6 December 2022 at 23:41:09 UTC, H. S. Teoh wrote:On Tue, Dec 06, 2022 at 11:07:32PM +0000, johannes via Digitalmars-d-learn wrote:no, you don't "need" to use std conv Here is an alternative that doesn't allocate Make sure to read the comments ```D // here notice where the functions are comming from import std.stdio : writeln, writefln; import core.stdc.stdio : printf; import core.stdc.string : strlen; const(char)* sqlite3_column_text(void*, int iCol) { return "hello world"; } void main() { // printf is a libc function, it expects a null terminated string (char*) printf("%s\n", sqlite3_column_text(null, 0)); // writeln is a d function, it expects a string (immutable slice of char aka immutable(char)[], a slice is T* + len) // so here we get the string from sqlite const(char)* ret = sqlite3_column_text(null, 0); // then we need to determine the length of the null terminated string from c // we can use strlen from libc size_t len = strlen(ret); // now we got a len, we can build a D string, remember, it's just a slice string str = cast(string) ret[0 .. len]; writeln(str); writefln("%s", str); } ```//-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i));[...] In D, strings are not the same as char*. You should use std.conv.fromStringZ to convert the C char* to a D string. T
Dec 06 2022
On 07.12.22 01:35, ryuukk_ wrote:On Tuesday, 6 December 2022 at 23:41:09 UTC, H. S. Teoh wrote:[...][...]In D, strings are not the same as char*. You should use std.conv.fromStringZ to convert the C char* to a D string.no, you don't "need" to use std conv Here is an alternative that doesn't allocate[...]// now we got a len, we can build a D string, remember, it's just a slice string str = cast(string) ret[0 .. len];Slicing is exactly what std.string.fromStringz does. <https://dlang.org/phobos/std_string.html#.fromStringz>: "The returned array is a slice of the original buffer. The original data is not changed and not copied."
Dec 06 2022
On Tuesday, 6 December 2022 at 23:41:09 UTC, H. S. Teoh wrote:On Tue, Dec 06, 2022 at 11:07:32PM +0000, johannes via Digitalmars-d-learn wrote:Well, if we think the other way around, in the example below we'd copy string, right? ```d void stringCopy(Chars)(string source, ref Chars target) { import std.algorithm : min; auto len = min(target.length - 1, source.length); target[0 .. len] = source[0 .. len]; target[len] = '\0'; } import std.stdio; void main() { // |----- 21 chars ------| |-- 8 --| string sample = "bu bir deneme olmakta\0gizlimi?"; char[30] cTxt; // 29 + 1'\0' sample.stringCopy = cTxt; // disappeared ? char cTxt.writeln; // appeared 28 chars printf("%s\n", cTxt.ptr); // appeared 21 chars printf("%s\n", &cTxt[22]); // appeared 7 chars writefln!"%(%02X %)"(cast(ubyte[])cTxt); } ``` Attention, the method used is again a slicing... SDB 79//-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i));[...] In D, strings are not the same as char*. You should use std.conv.fromStringZ to convert the C char* to a D string.
Dec 08 2022
On 12/8/22 08:21, Salih Dincer wrote:void stringCopy(Chars)(string source, ref Chars target)sample.stringCopy = cTxt; // disappeared ? charNothing disappeared on my system. (?) Going off-topic, I find the expression above extremely confusing. I am used to assignment expression changing the value of the left hand side, but that expression changes cTxt. Very confusing... Such obfuscations make it very hard for me to understand what the code is trying to demonstrate. Ali
Dec 08 2022
On Thursday, 8 December 2022 apt 17:39:58 UTC, Ali Çehreli wrote:You're right, it's my fault. Copy commands are always used as source on the left and target on the right. I have to get rid of this habit. Immediately I'm swapping parameters and fixing... ```d stringCopy(cText, sample); // or cText.stringCopy(sample): // or cText.stringCopy = sample; // tricky 😀 ``` SDB79```d void stringCopy(Chars)(string source, ref Chars target) sample.stringCopy = cTxt; // disappeared ? char ```I find the expression above extremely confusing. I am used to assignment expression changing the value of the left hand side, but that expression changes cTxt.
Dec 08 2022
On Thursday, 8 December 2022 at 17:39:58 UTC, Ali Çehreli wrote:On 12/8/22 08:21, Salih Dincer wrote:Yes, with function call syntax it's more understandable if an argument is modified. It was bizarre and confusing that assignment syntax was implemented for functions and methods not explicitly marked with property.void stringCopy(Chars)(string source, ref Chars target)sample.stringCopy = cTxt; // disappeared ? charNothing disappeared on my system. (?) Going off-topic, I find the expression above extremely confusing. I am used to assignment expression changing the value of the left hand side, but that expression changes cTxt. Very confusing... Such obfuscations make it very hard for me to understand what the code is trying to demonstrate.
Dec 10 2022
On Saturday, 10 December 2022 at 11:13:57 UTC, Nick Treleaven wrote:It was bizarre and confusing that assignment syntax was implemented for functions and methods not explicitly marked with property.Hi Nick, do you think this is confusing? ```d void main() { struct S { int value; alias opCall this; this(int i) { value = i; } alias opAssign = opCall; property opCall(int x) { return value = x; } property opCall() inout { return value; } property opOpAssign(string op)(int x) { mixin("return value"~op~"=x;"); } } S a = S(10), b = S(-1); import std.stdio; writeln(a + b); // 9 writeln(++a + b); // 10 } ``` Actually, my question to everyone is, for example, is this kind of wrapping confusing for you? SDB 79
Dec 18 2022
On Tuesday, 6 December 2022 at 23:41:09 UTC, H. S. Teoh wrote:On Tue, Dec 06, 2022 at 11:07:32PM +0000, johannes via Digitalmars-d-learn wrote:Well, if we think the other way around, in the example below we'd copy string, right? ```d void stringCopy(Chars)(string source, ref Chars target) { import std.algorithm : min; auto len = min(target.length - 1, source.length); target[0 .. len] = source[0 .. len]; target[len] = '\0'; } import std.stdio; void main() { // |----- 21 chars ------| |-- 8 --| string sample = "bu bir deneme olmakta\0gizlimi?"; char[30] cTxt; // 29 + 1'\0' sample.stringCopy = cTxt; // disappeared ? char cTxt.writeln; // appeared 28 chars printf("%s\n", cTxt.ptr); // appeared 21 chars printf("%s\n", &cTxt[22]); // appeared 7 chars writefln!"%(%02X %)"(cast(ubyte[])cTxt); } ``` Attention, the method used is again a slicing... SDB 79//-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i));[...] In D, strings are not the same as char*. You should use std.conv.fromStringZ to convert the C char* to a D string.
Dec 08 2022
On Tuesday, 6 December 2022 at 23:07:32 UTC, johannes wrote://-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i)); writefln("%s",sqlite3_column_text(res, i)); writefln(std.conv.to!string(sqlite3_column_text(res, i))); //-- the result : the sun is shining 55B504B3CE98 55B504B3CE98 the sun is shining => without 'std.conv.to!string' I presume 'write' prints out the address of the first byte. This is odd to me because printf does the job correctly. But I think to understand that write in D interpretes char* as a pointer to a byte. So it prints the address that the pointer points to.(?) So the question : for every c funtion returning char* I will have to use std.conv.to!string(..) or is there another way. Also it seems the formatting "%s" has another meaning in D ?What you are posting here is a perfect example of unsafe code. Let's looks at it: ```D import std; char *sqlite3_column_text() system { return cast(char *)"the sun is shining".ptr; } void main() { printf("%s\n",sqlite3_column_text()); writeln(sqlite3_column_text()); writefln("%s",sqlite3_column_text()); writefln(std.conv.to!string(sqlite3_column_text())); } ``` Unsafe code has a lot of rules, which have to be carefully followed if you want to stay out of troubles. It's necessary to check the [sqlite3 documentation](https://www.sqlite.org/c3ref/column_blob.html) to see who is responsible for deallocating the returned c-string and what is its expected lifetime (basically, the sqlite3 library takes care of managing the returned memory buffer and it's valid only until you make some other sqlite3 API calls). So the use of "printf" is fine. Direct "writeln"/"writefln" prints the pointer value, which also fine (but not what you expect). Doing "std.conv.to!string" allocates a copy of the string, managed by the garbage collector (and this may be undesirable for performance reasons). Doing [std.conv.fromStringz](https://dlang.org/library/std/string/from_stringz.html) as suggested by H. S. Teoh would avoid allocation, but you need to be careful with it: ```D char[] s1 = fromStringz(sqlite3_column_text()); writeln(s1); // everything is fine char[] s2 = fromStringz(sqlite3_column_text()); // some other sqlite3 API call writeln(s1); // oops, sqlite3 may have already messed up s1 ``` Depending on what sqlite3 is doing under the hood, this may be a good example of "use after free" security issue discussed [in another forum thread](https://forum.dlang.org/post/mailman.2828.1670270281.31357.digitalmars-d puremagic.com). But hold on! Isn't D a safe language, supposed to protect you from danger? The answer is that it is safe, but the compiler will only watch your back if you explicitly annotate your code with a [ safe attribute](https://dlang.org/spec/memory-safe-d.html) (and use the "-dip1000" compiler command line option too). If you do this, then the compiler will start complaining a lot. Try it: ```D safe: import std; char *sqlite3_column_text() system { return cast(char *)"the sun is shining".ptr; } string trusted_sqlite3_column_text() trusted { return std.conv.to!string(sqlite3_column_text()); } void main() { printf("%s\n",sqlite3_column_text()); // Nay! writeln(sqlite3_column_text()); // Nay! writefln("%s",sqlite3_column_text()); // Nay! writefln(std.conv.to!string(sqlite3_column_text())); // Nay! writeln(trusted_sqlite3_column_text()); // Yay! } ``` The "trusted_sqlite3_column_text" wrapper function does a memory allocation and it's up to you to decide whether this is acceptable. You are always free to write unsafe code tuned for best performance (annotated as system or trusted), but it's your own responsibility if you make a mistake. BTW, maybe it's even more efficient to use sqlite3_column_blob() and sqlite3_column_bytes() for retrieving the column text as a string?
Dec 06 2022
On Wednesday, 7 December 2022 at 01:46:21 UTC, Siarhei Siamashka wrote:On Tuesday, 6 December 2022 at 23:07:32 UTC, johannes wrote:That's a good point, but there also no error checking for the sqlite return, but that's not the point, ``std.conv.to!string`` doesn't teach about the difference between c string and d string, it hides the magic Doing it manually teaches it, that was the point//-- the result should be f.i. "the sun is shining" //-- sqlite3_column_text returns a constant char* a \0 delimited c-string printf("%s\n",sqlite3_column_text(res, i)); writeln(sqlite3_column_text(res, i)); writefln("%s",sqlite3_column_text(res, i)); writefln(std.conv.to!string(sqlite3_column_text(res, i))); //-- the result : the sun is shining 55B504B3CE98 55B504B3CE98 the sun is shining => without 'std.conv.to!string' I presume 'write' prints out the address of the first byte. This is odd to me because printf does the job correctly. But I think to understand that write in D interpretes char* as a pointer to a byte. So it prints the address that the pointer points to.(?) So the question : for every c funtion returning char* I will have to use std.conv.to!string(..) or is there another way. Also it seems the formatting "%s" has another meaning in D ?What you are posting here is a perfect example of unsafe code. Let's looks at it: ```D import std; char *sqlite3_column_text() system { return cast(char *)"the sun is shining".ptr; } void main() { printf("%s\n",sqlite3_column_text()); writeln(sqlite3_column_text()); writefln("%s",sqlite3_column_text()); writefln(std.conv.to!string(sqlite3_column_text())); } ``` Unsafe code has a lot of rules, which have to be carefully followed if you want to stay out of troubles. It's necessary to check the [sqlite3 documentation](https://www.sqlite.org/c3ref/column_blob.html) to see who is responsible for deallocating the returned c-string and what is its expected lifetime (basically, the sqlite3 library takes care of managing the returned memory buffer and it's valid only until you make some other sqlite3 API calls). So the use of "printf" is fine. Direct "writeln"/"writefln" prints the pointer value, which also fine (but not what you expect). Doing "std.conv.to!string" allocates a copy of the string, managed by the garbage collector (and this may be undesirable for performance reasons). Doing [std.conv.fromStringz](https://dlang.org/library/std/string/from_stringz.html) as suggested by H. S. Teoh would avoid allocation, but you need to be careful with it: ```D char[] s1 = fromStringz(sqlite3_column_text()); writeln(s1); // everything is fine char[] s2 = fromStringz(sqlite3_column_text()); // some other sqlite3 API call writeln(s1); // oops, sqlite3 may have already messed up s1 ``` Depending on what sqlite3 is doing under the hood, this may be a good example of "use after free" security issue discussed [in another forum thread](https://forum.dlang.org/post/mailman.2828.1670270281.31357.digitalmars-d puremagic.com). But hold on! Isn't D a safe language, supposed to protect you from danger? The answer is that it is safe, but the compiler will only watch your back if you explicitly annotate your code with a [ safe attribute](https://dlang.org/spec/memory-safe-d.html) (and use the "-dip1000" compiler command line option too). If you do this, then the compiler will start complaining a lot. Try it: ```D safe: import std; char *sqlite3_column_text() system { return cast(char *)"the sun is shining".ptr; } string trusted_sqlite3_column_text() trusted { return std.conv.to!string(sqlite3_column_text()); } void main() { printf("%s\n",sqlite3_column_text()); // Nay! writeln(sqlite3_column_text()); // Nay! writefln("%s",sqlite3_column_text()); // Nay! writefln(std.conv.to!string(sqlite3_column_text())); // Nay! writeln(trusted_sqlite3_column_text()); // Yay! } ``` The "trusted_sqlite3_column_text" wrapper function does a memory allocation and it's up to you to decide whether this is acceptable. You are always free to write unsafe code tuned for best performance (annotated as system or trusted), but it's your own responsibility if you make a mistake. BTW, maybe it's even more efficient to use sqlite3_column_blob() and sqlite3_column_bytes() for retrieving the column text as a string?
Dec 06 2022
On 12/6/22 15:07, johannes wrote:'write' prints out the address of the first byte. This is odd to me because printf does the job correctly.printf behaves as what you expect because %s means dereferencing the pointer values and printing the char contents until printf sees '\0'.But I think to understand that write in D interpretes char* as a pointer to a byte.Because it is. :) char* is nothing but a pointer to char. ('byte' exists as well, so I user 'char'.)it seems the formatting "%s" has another meaning in D ?Yes. %s means the string representation of the variable. Sring representation of a pointer happens to be the hexadecimal representation of its value. User-defined types can define a toSring() member function to decide how their string representation should be. %s is the default: write(x) or writeln(x) would print like %s would. This all works because these functions are templates, taking advantage of type deduction: They know the exact type of what they are printing. So, %s is known for that type. printf that came from C is not templatized, so the programmer has to tell it what to do like with %s. Ali
Dec 07 2022
Thank you all for those explanations. Helps a lot!
Dec 08 2022