printf, writeln, writefln
ryuukk_
ryuukk.dev at gmail.com
Wed Dec 7 03:50:20 UTC 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:
>> //-- 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
More information about the Digitalmars-d-learn
mailing list