digitalmars.D.learn - Problems with string literals and etc.c.odbc.sql functions
- Fer22f (21/21) Dec 18 2015 By the use of this tutorial
- Adam D. Ruppe (6/11) Dec 18 2015 That's what the examples on MSDN do too though, a cast. At first
- Fer22f (6/9) Dec 18 2015 The ODBC functions also have a size parameter for string
- Kagamin (10/10) Dec 19 2015 Well, ISO 9075-3 doesn't use const qualifiers, but uses IN/OUT
- anonymous (10/31) Dec 18 2015 If the parameter is de facto const, then the cast is ok. Though, maybe
- Fer22f (14/16) Dec 18 2015 I'm just worried about casts because I read somewhere that
- anonymous (8/15) Dec 18 2015 That sentence doesn't apply. string is not a class, it's an alias for
- Marc =?UTF-8?B?U2Now7x0eg==?= (5/8) Dec 19 2015 As this is going to be passed to a C function, it would need to
- anonymous (3/7) Dec 19 2015 Ouch, I totally missed that. Looks like we don't have a nice way to do
- Marc =?UTF-8?B?U2Now7x0eg==?= (8/17) Dec 20 2015 I guess so. Theoretically, we could change `toStringz()` to
- Kagamin (4/5) Dec 19 2015 No, ODBC API is designed with multilingual capability in mind, it
- Marc =?UTF-8?B?U2Now7x0eg==?= (3/9) Dec 20 2015 Nice, then SQL_NTS means "null terminated string" and should be
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
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
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
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
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
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
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
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
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
On Saturday, 19 December 2015 at 14:16:36 UTC, anonymous wrote:On 19.12.2015 14:20, Marc Schütz wrote: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`.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 20 2015
On Saturday, 19 December 2015 at 13:20:03 UTC, Marc Schütz wrote:As this is going to be passed to a C functionNo, 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
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:Nice, then SQL_NTS means "null terminated string" and should be replaced by `mystring.length`...As this is going to be passed to a C functionNo, 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 20 2015