www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - printf, writeln, writefln

reply johannes <johannes.janssens yahoo.com> writes:
//-- 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
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
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
next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
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:
 //-- 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
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); } ```
Dec 06 2022
parent ag0aep6g <anonymous example.com> writes:
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
prev sibling next sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
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:
 //-- 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.
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
Dec 08 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/8/22 08:21, Salih Dincer wrote:

 void stringCopy(Chars)(string source,
                      ref Chars target)
    sample.stringCopy = cTxt;  // disappeared ? char
Nothing 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
next sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 8 December 2022 apt 17:39:58 UTC, Ali Çehreli wrote:
 ```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.
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
Dec 08 2022
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Thursday, 8 December 2022 at 17:39:58 UTC, Ali Çehreli wrote:
 On 12/8/22 08:21, Salih Dincer wrote:

 void stringCopy(Chars)(string source,
                      ref Chars target)
    sample.stringCopy = cTxt;  // disappeared ? char
Nothing 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.
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.
Dec 10 2022
parent Salih Dincer <salihdb hotmail.com> writes:
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
prev sibling parent Salih Dincer <salihdb hotmail.com> writes:
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:
 //-- 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.
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
Dec 08 2022
prev sibling next sibling parent reply Siarhei Siamashka <siarhei.siamashka gmail.com> writes:
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
parent ryuukk_ <ryuukk.dev gmail.com> writes:
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:
 //-- 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?
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
Dec 06 2022
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
prev sibling parent johannes <johannes.janssens yahoo.com> writes:
Thank you all for those explanations. Helps a lot!
Dec 08 2022