printf, writeln, writefln
Siarhei Siamashka
siarhei.siamashka at gmail.com
Wed Dec 7 01:46:21 UTC 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?
More information about the Digitalmars-d-learn
mailing list