Thread overview
Template: get function name
Aug 17, 2020
novice3
Aug 17, 2020
Simen Kjærås
Aug 17, 2020
novice3
Aug 17, 2020
novice3
Aug 17, 2020
novice3
Aug 17, 2020
MoonlightSentinel
Aug 17, 2020
novice3
August 17, 2020
Hello.
I have wrapping Windows API functions, wich return 0 on success and erroro code on failure.

I copy wenforce template as:
```
private T denforce(T, S)(T value, lazy S msg = null, string file = __FILE__, size_t line = __LINE__)
{
  import core.sys.windows.winerror: NO_ERROR;
  import std.conv: to;
  if (value != NO_ERROR)
    throw new WindowsException(value, to!string(msg), file, line);
  return value;
}
```

and use it:
```
DhcpEnumServers(0, null, servers, null, null).denforce("DhcpEnumServers");
````

Then windows api - extern (Windows)DhcpEnumServers - return error, then Windows exception throwed with name of failed api "DhcpEnumServers".

Can we change template to avoid api name dublicate?
Can denforce template obtain "DhcpEnumServers" function name to format message "DhcpEnumServers api failed"?

Thanks.

August 17, 2020
On Monday, 17 August 2020 at 08:07:32 UTC, novice3 wrote:
> Hello.
> I have wrapping Windows API functions, wich return 0 on success and erroro code on failure.
>
> I copy wenforce template as:
> ```
> private T denforce(T, S)(T value, lazy S msg = null, string file = __FILE__, size_t line = __LINE__)
> {
>   import core.sys.windows.winerror: NO_ERROR;
>   import std.conv: to;
>   if (value != NO_ERROR)
>     throw new WindowsException(value, to!string(msg), file, line);
>   return value;
> }
> ```
>
> and use it:
> ```
> DhcpEnumServers(0, null, servers, null, null).denforce("DhcpEnumServers");
> ````
>
> Then windows api - extern (Windows)DhcpEnumServers - return error, then Windows exception throwed with name of failed api "DhcpEnumServers".
>
> Can we change template to avoid api name dublicate?
> Can denforce template obtain "DhcpEnumServers" function name to format message "DhcpEnumServers api failed"?
>
> Thanks.

Take the function as an alias parameter and wrap the entire call:

auto denforce(alias fn, string file = __FILE__, size_t line = __LINE__, Args...)(Args args) {
    import core.sys.windows.winerror: NO_ERROR;
    auto value = fn(args);
    if (value != NO_ERROR) {
        throw new WindowsException(value, __traits(identifier, fn)~" api call failed.", file, line);
    }
    return value;
}

unittest {
    denforce!DhcpEnumServers(0, null, servers, null, null);
}

For bonus points, you could also get the error message for the returned error code:

string convertErrorCode(uint code) {
    import core.sys.windows.winbase : FormatMessage, LoadLibrary, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_FROM_HMODULE;
    wchar[512] buf;

    auto written = FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        null,
        code,
        0,
        buf.ptr, buf.length,
        null);

    if (!written) {
        auto inst = LoadLibrary("Ntdsbmsg.dll");
        if (inst) {
            written = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
                inst,
                code,
                0,
                buf.ptr, buf.length,
                null);
        }
    }
    if (written) {
        import std.conv : to;
        return buf.ptr.to!string;
    } else {
        import std.format : format;
        return format("An unknown error occured: %x", code);
    }
}

unittest {
    import core.sys.windows.winerror : ERROR_INVALID_FUNCTION;

    import std.stdio;
    writeln(convertErrorCode(ERROR_INVALID_FUNCTION));
    writeln(convertErrorCode(1234567891));
}

--
  Simen
August 17, 2020
On Monday, 17 August 2020 at 08:55:49 UTC, Simen Kjærås wrote:
> Take the function as an alias parameter and wrap the entire call:
>
> auto denforce(alias fn, string file = __FILE__, size_t line = __LINE__, Args...)(Args args)

Thank you, Simen!
August 17, 2020
> On Monday, 17 August 2020 at 08:55:49 UTC, Simen Kjærås wrote:
>> Take the function as an alias parameter and wrap the entire call:

Simen, for some reasons, your code dont respect api arg with "out" attribute.

For example, i have
```
extern (Windows)
DhcpEnumServers(
    in      DWORD                    Flags,         // must be zero
    in      LPVOID                   IdInfo,        // must be NULL
    out     LPDHCP_SERVER_INFO_ARRAY Servers,       // output servers list
    in      LPVOID                   CallbackFn,    // must be NULL
    in      LPVOID                   CallbackData   // must be NULL
);
```

and then i use it:
```
LPDHCP_SERVER_INFO_ARRAY servers;
denforce!DhcpEnumServers(0, null, servers, null, null);
```

access violation occur.
In disassembler we can see that servers parameter passed by value (initial 0 instead of address).
In my earler code it wass passed properly, by ref.

May be this not matter of your code,
and i can bypass it by change extern declaration to

  LPDHCP_SERVER_INFO_ARRAY *Servers,

but it will be interest to know the reasons...

Any way, tnaks you for your code, i will use it.
August 17, 2020
On Monday, 17 August 2020 at 09:45:55 UTC, novice3 wrote:
> access violation occur.

reduced code https://run.dlang.io/is/U58t9R

void test(out int x) { x = 42; }

void call (alias fn, Args ...)(Args args) { fn(args); }

void main(){
    int a;

    a = 111;
    test(a);
    assert(a == 42);  // OK

    a = 222;
    call!test(a);
    assert(a == 42);  // FAIL
}

August 17, 2020
On Monday, 17 August 2020 at 09:59:21 UTC, novice3 wrote:
> On Monday, 17 August 2020 at 09:45:55 UTC, novice3 wrote:
>> access violation occur.
>
> reduced code https://run.dlang.io/is/U58t9R

The wrapper parameters don't inherit the storage classes from  the wrapped function. Try using std.traits.Parameters instead: https://run.dlang.io/is/wTyJWD.

```
import std.traits : Parameters;

void test(out int x) { x = 42; }

void call (alias fn)(Parameters!fn args) { fn(args); }

void main()
{
    int a;

    a = 111;
    test(a);
    assert(a == 42);

    a = 222;
    call!test(a);
    assert(a == 42);
}
```
August 17, 2020
On Monday, 17 August 2020 at 10:11:29 UTC, MoonlightSentinel wrote:
> On Monday, 17 August 2020 at 09:59:21 UTC, novice3 wrote:
>> On Monday, 17 August 2020 at 09:45:55 UTC, novice3 wrote:
>>> access violation occur.
>>
>> reduced code https://run.dlang.io/is/U58t9R
>
> The wrapper parameters don't inherit the storage classes from  the wrapped function. Try using std.traits.Parameters instead: https://run.dlang.io/is/wTyJWD.

Nice, it works!