www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problems with string literals and etc.c.odbc.sql functions

reply Fer22f <fer22f gmail.com> writes:
By the use of this tutorial 
(http://www.easysoft.com/developer/languages/c/odbc_tutorial.html), I thought
it would be very straightforward to use etc.c.odbc.sqlext and etc.c.odbc.sql to
create a simple odbc application. But as soon as I started, I noticed a quirk:

     SQLRETURN ret;
     SQLHDBC dbc;
     ret = SQLDriverConnect(dbc, null, "DNS=*mydns*;", SQL_NTS,
             null, 0, null, SQL_DRIVER_COMPLETE);

This gives me an error: function 
etc.c.odbc.sqlext.SQLDriverConnect (void* hdbc, void* hwnd, char* 
szConnStrIn, short cbConnStrIn, char* szConnStrOut, short 
cbConnStrOutMax, short* pcbConnStrOut, ushort fDriverCompletion) 
is not callable using argument types (void*, typeof(null), 
string, int, typeof(null), int, typeof(null), int)

After some casting, I found out it's all related to the string 
literal. I thought it would work straight off the box, after 
reading the "Interfacing to C" spec 
(http://dlang.org/spec/interfaceToC.html).

When I remove the string literal and replace it with null, it 
compiles. .ptr and .toStringz both give immutable char* 
references, and don't work. A "cast(char *)"DNS=*maydns*;"" 
works, but it feels a lot like a hack that will not work in the 
long run.
Dec 18 2015
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 18 December 2015 at 22:14:04 UTC, Fer22f wrote:
 When I remove the string literal and replace it with null, it 
 compiles. .ptr and .toStringz both give immutable char* 
 references, and don't work. A "cast(char *)"DNS=*maydns*;"" 
 works, but it feels a lot like a hack that will not work in the 
 long run.
That's what the examples on MSDN do too though, a cast. At first I thought the binding was missing a const, but the ODBC docs don't specify it as const either and cast. So it is kinda weird but I think right according to docs. However, I'd argue we should make it const if it can be...
Dec 18 2015
next sibling parent Fer22f <fer22f gmail.com> writes:
On Friday, 18 December 2015 at 22:18:34 UTC, Adam D. Ruppe wrote:
 That's what the examples on MSDN do too though, a cast. At 
 first I thought the binding was missing a const, but the ODBC 
 docs don't specify it as const either and cast.
The ODBC functions also have a size parameter for string parameters. You can set it to SQL_NTS to use the 0 terminated C standard. Might justify on why it's char* instead of const char*. It looks like it's alright, then. Just one implementation detail I didn't notice before.
Dec 18 2015
prev sibling parent Kagamin <spam here.lot> writes:
Well, ISO 9075-3 doesn't use const qualifiers, but uses IN/OUT 
qualifiers instead, e.g. ExecDirect function is declared as:

ExecDirect (
    StatementHandle IN INTEGER,
    StatementText   IN CHARACTER(L),
    TextLength      IN INTEGER )
    RETURNS SMALLINT

And in C header:

SQLRETURN SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR 
*StatementText, SQLINTEGER TextLength);
Dec 19 2015
prev sibling parent reply anonymous <anonymous example.com> writes:
On 18.12.2015 23:14, Fer22f wrote:
 By the use of this tutorial
 (http://www.easysoft.com/developer/languages/c/odbc_tutorial.html), I
 thought it would be very straightforward to use etc.c.odbc.sqlext and
 etc.c.odbc.sql to create a simple odbc application. But as soon as I
 started, I noticed a quirk:

      SQLRETURN ret;
      SQLHDBC dbc;
      ret = SQLDriverConnect(dbc, null, "DNS=*mydns*;", SQL_NTS,
              null, 0, null, SQL_DRIVER_COMPLETE);

 This gives me an error: function etc.c.odbc.sqlext.SQLDriverConnect
 (void* hdbc, void* hwnd, char* szConnStrIn, short cbConnStrIn, char*
 szConnStrOut, short cbConnStrOutMax, short* pcbConnStrOut, ushort
 fDriverCompletion) is not callable using argument types (void*,
 typeof(null), string, int, typeof(null), int, typeof(null), int)

 After some casting, I found out it's all related to the string literal.
 I thought it would work straight off the box, after reading the
 "Interfacing to C" spec (http://dlang.org/spec/interfaceToC.html).

 When I remove the string literal and replace it with null, it compiles.
 .ptr and .toStringz both give immutable char* references, and don't
 work. A "cast(char *)"DNS=*maydns*;"" works, but it feels a lot like a
 hack that will not work in the long run.
If the parameter is de facto const, then the cast is ok. Though, maybe it should be marked const then. If the parameter is really not const, i.e. the function may mutate the argument, then the cast is not ok. You can use `.dup.ptr` instead to get a proper char* from a string. Also, remember that D's GC doesn't scan foreign memory. So if the function keeps the string around, and that's the only reference, then the GC would collect it. The function probably doesn't keep the string around, though.
Dec 18 2015
next sibling parent reply Fer22f <fer22f gmail.com> writes:
On Friday, 18 December 2015 at 22:35:04 UTC, anonymous wrote:
 If the parameter is de facto const, then the cast is ok. 
 Though, maybe it should be marked const then.
I'm just worried about casts because I read somewhere that strings start with the number of characters inside them (probably in slices documentation), and not with actual content (though string literals probably act different in this case). Documentation on casts say: Casting a pointer type to and from a class type is done as a type paint (i.e. a reinterpret cast). Reinterpretation is rather dangerous if strings are stored differently. But this test gives me a good hope on this case: writeln(*(cast(char*) "Test")); Casting is what I'm going with. .dup.ptr is less clear in this case.
Dec 18 2015
parent anonymous <anonymous example.com> writes:
On 19.12.2015 01:06, Fer22f wrote:
 Documentation on casts say:

 Casting a pointer type to and from a class type is done as a type paint
 (i.e. a reinterpret cast).
That sentence doesn't apply. string is not a class, it's an alias for immutable(char)[], i.e. it's an array.
 Reinterpretation is rather dangerous if strings are stored differently.

 But this test gives me a good hope on this case:

      writeln(*(cast(char*) "Test"));

 Casting is what I'm going with. .dup.ptr is less clear in this case.
Correctness beats clarity. I'd like to advise you not to use casts unless you know for sure that they're safe. Here, you need to know what a string is exactly, what the cast does exactly, and what exactly the called function does with the pointer.
Dec 18 2015
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 18 December 2015 at 22:35:04 UTC, anonymous wrote:
 If the parameter is really not const, i.e. the function may 
 mutate the argument, then the cast is not ok. You can use 
 `.dup.ptr` instead to get a proper char* from a string.
As this is going to be passed to a C function, it would need to be zero-terminated. `.dup` doesn't do this, he'd have to use `std.string.toStringz` instead. However, that function returns a `immutable(char)*`, which would have to be cast again :-(
Dec 19 2015
next sibling parent reply anonymous <anonymous example.com> writes:
On 19.12.2015 14:20, Marc Schütz wrote:
 As this is going to be passed to a C function, it would need to be
 zero-terminated. `.dup` doesn't do this, he'd have to use
 `std.string.toStringz` instead. However, that function returns a
 `immutable(char)*`, which would have to be cast again :-(
Ouch, I totally missed that. Looks like we don't have a nice way to do this then?
Dec 19 2015
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Saturday, 19 December 2015 at 14:16:36 UTC, anonymous wrote:
 On 19.12.2015 14:20, Marc Schütz wrote:
 As this is going to be passed to a C function, it would need 
 to be
 zero-terminated. `.dup` doesn't do this, he'd have to use
 `std.string.toStringz` instead. However, that function returns 
 a
 `immutable(char)*`, which would have to be cast again :-(
Ouch, I totally missed that. Looks like we don't have a nice way to do this then?
I guess so. Theoretically, we could change `toStringz()` to return `char*`; if its result is unique (we would have to change the implementation to always make a copy), it should then be implicitly convertible to `immutable(char)*`. But that would break code, because it could have been used like `auto s = "xyz".toStringz;`, where `s` would then have a different type. So, there'd need to be an additional function, `toMutableStringz`.
Dec 20 2015
prev sibling parent reply Kagamin <spam here.lot> writes:
On Saturday, 19 December 2015 at 13:20:03 UTC, Marc Schütz wrote:
 As this is going to be passed to a C function
No, ODBC API is designed with multilingual capability in mind, it doesn't rely on null terminated strings heavily: all string arguments support length specification.
Dec 19 2015
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Saturday, 19 December 2015 at 17:30:02 UTC, Kagamin wrote:
 On Saturday, 19 December 2015 at 13:20:03 UTC, Marc Schütz 
 wrote:
 As this is going to be passed to a C function
No, ODBC API is designed with multilingual capability in mind, it doesn't rely on null terminated strings heavily: all string arguments support length specification.
Nice, then SQL_NTS means "null terminated string" and should be replaced by `mystring.length`...
Dec 20 2015