Re: Why does this mixin fail to compile?

2024-07-02 Thread Steven Schveighoffer via Digitalmars-d-learn

On Tuesday, 2 July 2024 at 07:23:42 UTC, ryuukk_ wrote:

I said it 2 times already, i don't want string concatenation, 
i'll benchmark later, but not right now, right now i'm looking 
for a functioning code without string concatenation


Your buffer solution works, but you need to put it inside a 
*function*, not at declaration scope. What you wrote declares 
*runtime* variables, which wouldn't be usable at compile time (if 
you got it past the parser, which is where it was failing).


So for instance:

```d
mixin template implement()
{
mixin(() {
// ctfe new array is basically the same as static array
char[] buffer = new char[4096];
int pos = 0;

void append(string str)
{
buffer[pos .. pos + str.length] = str[];
pos += str.length;
}

append("void* ctx;");
return buffer[0 .. pos];
}());
}
```

And yes, it is faster to do this than appending. But you have to 
do a *lot* of it to make a huge difference.


-Steve


Re: Why does this mixin fail to compile?

2024-07-02 Thread Jonathan M Davis via Digitalmars-d-learn
On Tuesday, July 2, 2024 1:23:42 AM MDT ryuukk_ via Digitalmars-d-learn wrote:
> I said it 2 times already, i don't want string concatenation,
> i'll benchmark later, but not right now, right now i'm looking
> for a functioning code without string concatenation

D has two options for mixins, and both of them require complete statements.

Template mixins mix in the contents of a template rather than strings, but
they only allow you to compile in declarations, because that's all that
templates can contain:

https://dlang.org/spec/template-mixin.html

You can control what is compiled into a template mixin via conditional
compilation based on the template arguments (e.g. with static if or static
foreach), but the declarations do need to be complete just like they would
for any template, so it will really only work for you if you just need a set
of declarations and not if you're looking to build a list of statements to
run.

For anything more complex, you'll need string mixins:

https://dlang.org/spec/statement.html#mixin-statement

A string mixin takes any string that's known at compile time and compiles it
as code, but the string must contain a complete statement (or a sequence of
statements). You can provide the mixin a string literal, an enum, a list of
strings which are concatenated together, a function call, or any other
expression so long as it evaluates to a string at compile time.

You can of course choose to not use string concatenation to create such a
string, but unless you can provide a string literal with the exact piece of
code that you need, you're going to need to actually build a string from
pieces one way or another, and that normally means either appending to a
string and/or concatenating strings together.

The only way that I can think of at the moment which would involve building
a string at compile time but which would not involve explicit allocations
would be to use a static array and set the unused characters to whitespace,
since the compiler will let you mix in a static array of characters as a
string, e.g.

mixin(()
  {
  char[1024] buffer = ' ';
  ... build the string by setting the characters in buffer ...
  return buffer;
  }());

However, given that this is CTFE we're talking about here, I'm pretty sure
that this particular solution not only allocates, but it potentially
allocates more than simply appending to a string would. IIRC, CTFE's current
implementation doesn't actually use mutatation. Rather, it creates a new
value every time that you mutate a variable. And if that is indeed the case,
then each time you set a character in the static array, you'd basically be
duping the array. CTFE does not work in a manner that pretty much anyone who
hasn't actively worked on it is going to expect, and the generated code
isn't necessarily anything like what would be generated for runtime code.
So, not many D programmers are going to be very good at guessing what's
going to be most efficient with CTFE.

Regardless, because a string mixin must be given an expression that
evaluates to a string, your options are basically

1. Use string concatenation in an expression that you pass to mixin.

2. Call a function that returns a string (with the internals of that
function doing whatever is required to generate that string and return it)
and pass the return value to mixin.

3. Create an enum using either #1 or #2 and pass that to mixin.

So, if you want to build a statement from pieces instead of providing a
known statement or known list of statements, and you don't want to use
string concatenation, you basically have to write a function which builds
and returns a string in whatever the most efficient fashion is that you can
come up with.

And if you want to measure the performance of a particular function that
builds a string during CTFE, you _will_ need to measure build times rather
than testing it at runtime because of how differently CTFE behaves from
running code at runtime. And honestly, I expect that you would have to be
generating a lot of string mixins to be able to get a consistent, measurable
difference in build times between different implementations, but if you care
that much about build times, you'll need to experiment to see what the most
efficient way to build a string is for your code. It is certainly true
though that CTFE tends to be pretty inefficient, unfortunately. It really is
a shame that "newCTFE" was never completed.

In general, best practice would be to just build the string in whatever the
simplest, most maintainable fashion would be, particularly since it will
have no impact on the runtime performance of the program, and you could
easily spend more time trying to optimize your build times than you would
ever save by trying generate the string more efficiently, but it's your
code.

- Jonathan M Davis





Re: Default struct constructors if a struct member is a union

2024-07-02 Thread Steven Schveighoffer via Digitalmars-d-learn

On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:


My usecase scenario doesn't really allow constructors for the 
struct, since it's a binding to an external library via C API.


BTW, this is not true. A constructor does not change the struct 
layout or anything about it from the C side. You can safely add 
the struct constructor (or any other struct member functions) and 
the struct itself should be C compatible.


-Steve


Re: Default struct constructors if a struct member is a union

2024-07-02 Thread Steven Schveighoffer via Digitalmars-d-learn

On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:

```d
union U {
int i32;
long i64;
float f32;
double f64;
}

struct S {
TypeEnum type;
U data;
}

S foo0 = S(TypeEnum.Integer32, S(20));  //Ugly, but works
S foo1 = S(TypeEnum.Integer64, S(20L)); //Error: cannot 
implicitly convert expression

//ditto for the rest of the members
```

My question is can I initialize structs like these in one line 
without relying on a second line? My usecase scenario doesn't 
really allow constructors for the struct, since it's a binding 
to an external library via C API.


Have you tried named parameter construction?

```d
S foo2 = S(TypeEnum.Float32, U(f32: 20.0));
```

-Steve



Re: Why does this mixin fail to compile?

2024-07-02 Thread ryuukk_ via Digitalmars-d-learn

On Monday, 1 July 2024 at 21:43:02 UTC, Dennis wrote:

On Monday, 1 July 2024 at 13:00:55 UTC, ryuukk_ wrote:
please stick to what i wrote, i don't want string 
concatenation, i provide a reduced example from my project, 
everything should be a single template block, no extra 
functions other than the append() one


Mixin templates are a declaration scope, not a function scope, 
so they aren't necessarily analyzed top to bottom. This is why 
allowing partial string mixins would be complicated, consider 
this example:


```D
mixin template implement()
{
mixin("struct " ~ str);

mixin("{");

immutable string str = "T";

mixin("}");
}
```

It's not obvious how this should be compiled, and this is 
before throwing `static if` and `static foreach` in the mix! If 
you want to procedurally build a type in sequential steps, 
you'll have to do that in a function scope.


If your concern is that such a function would add needless code 
generation, you can use an immediately invoked anonymous 
function like so:


```D
mixin template implement(string typeName, string[] members)
{
mixin(() {
string result = "struct " ~ typeName ~ " {";
foreach (name; members)
{
result ~= "int " ~ name ~ ";";
}
result ~= "}";
return result;
} ());
}

mixin implement!("S", ["x", "y", "z"]);

immutable s = S(x: 3, y: 5, z: 7);
```

You can use your fixed size array append function to try and 
improve CTFE performance, but I'd start with straightforward 
concatenation, and see if it's actually too slow. In that case, 
maybe see if you can reduce it to a self-contained example and 
post it on bugzilla as a performance bug.


I said it 2 times already, i don't want string concatenation, 
i'll benchmark later, but not right now, right now i'm looking 
for a functioning code without string concatenation


odbc error Im008 sql server :(

2024-07-02 Thread alina via Digitalmars-d-learn
hello people I need help to connect to sqlserver this is my code 
and that is the error returned


```
import odbc.sql;
import odbc.sqlext;
import odbc.sqlucode;
import odbc.sqltypes;
import std.stdio;
import std.string : fromStringz, toStringz;

version(Windows) {
string connectionString = "Driver={SQL 
Server};Server=DESKTOP-E09IF8K;Database=ControlEmpleados;Trusted_Connection=True;SQLDriverConnect=True;";


} else {
string connectionString = "Driver={SQL 
Server};Server=DESKTOP-E09IF8K;Database=ControlEmpleados;Trusted_Connection=True;";

}

alias SQLLEN = int;  // Usamos `int` en lugar de `long` para 
SQLLEN


SQLHENV env = SQL_NULL_HENV;
SQLHDBC conn = SQL_NULL_HDBC;

int main(string[] argv) {
SQLRETURN ret;

// Allocate an environment handle
ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, );

// Set the ODBC version environment attribute
if (!SQL_SUCCEEDED(ret = SQLSetEnvAttr(env, 
SQL_ATTR_ODBC_VERSION, cast(SQLPOINTER*) SQL_OV_ODBC3, 0))) {
stderr.writefln("Failed to set ODBC version, SQL return 
code: %d", ret);

return 1;
}

// Allocate a connection handle
SQLAllocHandle(SQL_HANDLE_DBC, env, );

// Set login timeout to 3 seconds
SQLSetConnectAttr(conn, SQL_LOGIN_TIMEOUT, cast(SQLPOINTER) 
3, 0);


writefln("Connecting to db with: %s", connectionString);

// Connect to the database
if (SQL_SUCCEEDED(ret = SQLDriverConnect(conn, null, 
cast(char*) toStringz(connectionString), SQL_NTS, null, 0, null, 
SQL_DRIVER_COMPLETE))) {

SQLCHAR[256] dbms_ver;

writeln("Connected");

// Get the DBMS version
SQLGetInfo(conn, SQL_DBMS_VER, cast(SQLPOINTER)dbms_ver, 
dbms_ver.sizeof, null);


writefln(" - DBMS Version:\t%s", fromStringz(cast(char*) 
dbms_ver));


// Execute SQL query
SQLHSTMT stmt = SQL_NULL_HSTMT;
SQLAllocHandle(SQL_HANDLE_STMT, conn, );

string query = "SELECT @@version";
ret = SQLExecDirect(stmt, cast(SQLCHAR*) 
toStringz(query), SQL_NTS);


if (SQL_SUCCEEDED(ret)) {
SQLSMALLINT columns;
SQLNumResultCols(stmt, );

while (SQL_SUCCEEDED(ret = SQLFetch(stmt))) {
for (SQLUSMALLINT i = 1; i <= columns; i++) {
SQLCHAR[512] buf;
SQLLEN indicator;
ret = SQLGetData(stmt, i, SQL_C_CHAR, 
cast(SQLPOINTER) buf.ptr, buf.length, );

if (SQL_SUCCEEDED(ret)) {
if (indicator == SQL_NULL_DATA) {
write("\tNULL");
} else {
write("\t", fromStringz(cast(char*) 
buf.ptr));

}
}
}
writeln();
}
} else {
stderr.writefln("Failed to execute query. SQL return 
code: %d", ret);

writeErrorMessage(stmt);
}

// Free the statement handle
SQLFreeHandle(SQL_HANDLE_STMT, stmt);

// Disconnect from db and free allocated handles
SQLDisconnect(conn);
SQLFreeHandle(SQL_HANDLE_DBC, conn);
SQLFreeHandle(SQL_HANDLE_ENV, env);
} else {
stderr.writefln("Failed to connect to database. SQL 
return code: %d", ret);

writeErrorMessage();
return 1;
}

return 0;
}

// If a call to SQL returns -1 (SQL_ERROR) then this function can 
be called to get the error message

void writeErrorMessage(SQLHSTMT stmt = null) {
SQLCHAR[6] sqlstate;
SQLINTEGER nativeError;
SQLCHAR[SQL_MAX_MESSAGE_LENGTH] messageText;
SQLSMALLINT bufferLength = messageText.length;
SQLSMALLINT textLength;

SQLRETURN ret = SQLError(
env,
conn,
stmt,
[0],
,
[0],
bufferLength,

);

if (SQL_SUCCEEDED(ret)) {
writefln("SQL State %s, Error %d : %s", 
fromStringz(cast(char*) sqlstate), nativeError, 
fromStringz(cast(char*) messageText));

}
}

```

 Running sql-d.exe
Connecting to db with: Driver={SQL 
Server};Server=DESKTOP-E09IF8K;Database=ControlEmpleados;Trusted_Connection=True;SQLDriverConnect=True;
SQL State IM008, Error 0 : [Microsoft][ODBC SQL Server 
Driver]Error en el cuadro de di�logo

Failed to connect to database. SQL return code: -1
Error Program exited with code 1