www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Cannot check function address

reply frame <frame86 live.com> writes:
I have a function slot that may be loaded via a shared library.
I want to check if that function has an address but compiler (DMD 
2.100, Windows) instead tries to invocate the function?

```d
// --- module a:
alias F = extern (C) void function(string param);
F fun = someLibLoad!F("name");

assert(fun !is null); // compiles (also works in runtime)

// --- module b:
import a;

// same code:
//

// error: too few arguments, expected `5`, got `0` ... with 2.100
assert(fun !is null);
```

Usually that works fine as shown in module A but for some reason 
not in module B.
Workaround ideas? I tried casting to void* or ptrdiff_t but the 
compiler always tries to call the function -.-
May 24 2022
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/24/22 2:54 PM, frame wrote:
 I have a function slot that may be loaded via a shared library.
 I want to check if that function has an address but compiler (DMD 2.100, 
 Windows) instead tries to invocate the function?
 
 ```d
 // --- module a:
 alias F = extern (C) void function(string param);
 F fun = someLibLoad!F("name");
 
 assert(fun !is null); // compiles (also works in runtime)
This doesn't seem valid for module-level code, assert is an instruction, not a declaration.
 
 // --- module b:
 import a;
 
 // same code:
 //

 // error: too few arguments, expected `5`, got `0` ... with 2.100
 assert(fun !is null);
 ```
 
 Usually that works fine as shown in module A but for some reason not in 
 module B.
 Workaround ideas? I tried casting to void* or ptrdiff_t but the compiler 
 always tries to call the function -.-
`expected 5 got 0` suggests it is finding some other `fun`, as `a.fun` only takes a single parameter. Try `std.traits.fullyQualifiedName!fun` to see where it's coming from. -Steve
May 24 2022
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/24/22 3:09 PM, Steven Schveighoffer wrote:
 assert is an instruction, not a declaration.
errr.... it's a *statement*, not a declaration. -Steve
May 24 2022
prev sibling parent reply frame <frame86 live.com> writes:
On Tuesday, 24 May 2022 at 19:09:52 UTC, Steven Schveighoffer 
wrote:

 This doesn't seem valid for module-level code, assert is an 
 instruction, not a declaration.
 ...
 Try `std.traits.fullyQualifiedName!fun` to see where it's 
 coming from.

 expected 5 got 0 suggests it is finding some other fun, as 
 a.fun only takes a single parameter.
Of course this `assert()` is actually inside `static this()` and there is only one declaration. I just want to show that the used symbol and call is the same. Also this is **working** code. I just put the `assert()` there to ensure the function isn't called by a race condition before it was loaded (module constructors often have cyclic dependency, so I cannot put this in a more clean way). Those asserts() are set in: module A: module constructor module B: some function To also anwser to Adam: no, this symbol is unique. The first line of the error says: ``` Error: function `a.fun(string param)` is not callable using argument types `()`. ``` **Note that I have changed the actual module name and signature, it has orginal 5 arguments and very long names.** The point is, it shouldn't be called in this line anyway?
May 24 2022
next sibling parent Alain De Vos <devosalain ymail.com> writes:
Just looking at the error, are 2.098.1 & 2.100 , 100% compatible ?
May 24 2022
prev sibling next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Tuesday, 24 May 2022 at 22:10:21 UTC, frame wrote:
 To also anwser to Adam: no, this symbol is unique. The first 
 line of the error says:

 ```
 Error: function `a.fun(string param)` is not callable using 
 argument types `()`.
 ```
There's a big difference between a function and a function pointer. This error message says "function". But the code you showed as a function pointer. If it were a function pointer, the error would be: ``` Error: function pointer `a()` is not callable using argument types (int)` ``` So there is clearly some kind of mix up here.
 The point is, it shouldn't be called in this line anyway?
If it is a function, you get its address with the & operator. But if it is supposed to be a function pointer, something is wrong with the declaration...
May 24 2022
parent frame <frame86 live.com> writes:
On Tuesday, 24 May 2022 at 22:18:44 UTC, Adam D Ruppe wrote:

 There's a big difference between a function and a function 
 pointer.
Could you please clarify in this context? `Fun` is basically generated by code like that: ```d extern (Windows) void* GetProcAddress(void*, const char*); auto fn = cast(T)GetProcAddress(lib, mangledName.toStringz); ```
 If it is a function, you get its address with the & operator. 
 But if it is supposed to be a function pointer, something is 
 wrong with the declaration...
I have no idea what the correct term is but the debugger says `fun` is: (if that `assert()` is removed and it compiles): ``` Type: void function(...)* Value: 0x0000000000000000 null ``` I can get some address with `&fun` but deferencing then fails: ```d auto addr = &fun; assert(*addr); // Error: expression `*addr` of type `extern (C) void(...)` does not have a boolean value ```
May 24 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/24/22 6:10 PM, frame wrote:
 On Tuesday, 24 May 2022 at 19:09:52 UTC, Steven Schveighoffer wrote:
 
 This doesn't seem valid for module-level code, assert is an 
 instruction, not a declaration.
 ...
 Try `std.traits.fullyQualifiedName!fun` to see where it's coming from.

 expected 5 got 0 suggests it is finding some other fun, as a.fun only 
 takes a single parameter.
Of course this `assert()` is actually inside `static this()` and there is only one declaration. I just want to show that the used symbol and call is the same.
"of course" I have no idea what your real code looks like, unless you post the real code. While I get the point of trying to slim down the example to something postable, a very common problem with this kind of self-trimming is that you end up trimming out the problematic code. Why? Because if you noticed the problematic code, you wouldn't be posting here, you would just fix it. This, in fact, happens to me frequently. Now, it's also possible that there is an actual compiler bug here, it certainly seems like it could be, but a full (non-)working example would be useful. I still highly recommend trying code like: ```d import std.traits; pragma(msg, fullyQualifiedName!fun); ``` inside the place where your assert is failing. To me, if it's using the expected symbol for `fun` (or whatever it's called), then it should print the definition you expect from the module you expect. If not, maybe there is a name conflict, and it's picking the wrong one? These problems can be tricky to find. -Steve
May 24 2022
parent reply frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 01:23:56 UTC, Steven Schveighoffer 
wrote:

 "of course" I have no idea what your real code looks like, 
 unless you post the real code.

 While I get the point of trying to slim down the example to 
 something postable, a very common problem with this kind of 
 self-trimming is that you end up trimming out the problematic 
 code. Why? Because if you noticed the problematic code, you 
 wouldn't be posting here, you would just fix it. This, in fact, 
 happens to me frequently.

 Now, it's also possible that there is an actual compiler bug 
 here, it certainly seems like it could be, but a full 
 (non-)working example would be useful.

 I still highly recommend trying code like:

 ```d
 import std.traits;
 pragma(msg, fullyQualifiedName!fun);
 ```

 inside the place where your assert is failing. To me, if it's 
 using the expected symbol for `fun` (or whatever it's called), 
 then it should print the definition you expect from the module 
 you expect. If not, maybe there is a name conflict, and it's 
 picking the wrong one? These problems can be tricky to find.

 -Steve
Sorry, this was not affront, it was meant as my POV that you may have problems to get my problem because I have (as usually) forgot to make this more visible that some code was truncated. I tried your suggestion, as replied to Adam - the symbol is unique and it shows that correct location. It's not different from the error message. However, this error has nothing to do with `assert()`. In fact the compiler allows nothing than invocation or this error will be triggered. Even that fails: ```d // same error: auto test = cast(void*)fun; ```
May 24 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/24/22 10:06 PM, frame wrote:
 
 Sorry, this was not affront, it was meant as my POV that you may have 
 problems to get my problem because I have (as usually) forgot to make 
 this more visible that some code was truncated.
OK, sorry to read into that ;)
 I tried your suggestion, as replied to Adam - the symbol is unique and 
 it shows that correct location. It's not different from the error message.
Just to be pedantic, you tried that call in the *exact place* the assert is failing to compile? D can be weird/surprising about name lookups.
 
 However, this error has nothing to do with `assert()`. In fact the 
 compiler allows nothing than invocation or this error will be triggered. 
 Even that fails:
The compiler is treating `fun` not as a function pointer, but as a function. However, the type looks correct to me. I tried a simple example on run.dlang.io, even with multiple modules, and seems to work as expected.
 
 ```d
 // same error:
 auto test = cast(void*)fun;
 ```
 
try: pragma(msg, typeof(fun)); If I do: ```d extern(C) void foo(string) {} pragma(msg, typeof(foo)); pragma(msg, typeof(&foo)); ``` I get: extern (C) void(string param) extern (C) void function(string param) The first is a function (yes, they have a type), and the latter is a function pointer. So that might give a clue whether the compiler thinks `fun` is a function or a function pointer. D should *never* call a function pointer without parentheses. If you get the latter, yet your assert fails, then that is most certainly a compiler bug. Again, important that you do this in the exact place the assert fails to ensure the same name lookup situation. -Steve
May 24 2022
parent reply frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 02:42:26 UTC, Steven Schveighoffer 
wrote:

 Just to be pedantic, you tried that call in the *exact place* 
 the assert is failing to compile? D can be weird/surprising 
 about name lookups.
Yes, of course ;-)
 try:

 pragma(msg, typeof(fun));
Outputs: ``` extern (C) void function(...) ```
 If I do:

 ```d
 extern(C) void foo(string) {}

 pragma(msg, typeof(foo));
 pragma(msg, typeof(&foo));
 ```

 I get:

 extern (C) void(string param)
 extern (C) void function(string param)

 The first is a function (yes, they have a type), and the latter 
 is a function pointer.
But the compiler would complain if I call it as a type? And how could it call it later without an address to it - this works.
 D should never call a function pointer without parentheses.
I'm very good in finding weird bugs ;-) Unfortunately, I cannot make an example. I already tried some changes/simplifications but cannot reproduce it outside the project. But how can I help the compiler to use it as correct type? On invocation with arguments all works fine, but checking if the pointer is not null fails. It not even works with an assignment. Could some `static if()` maybe help or some asm trick?
May 24 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/24/22 11:14 PM, frame wrote:
 On Wednesday, 25 May 2022 at 02:42:26 UTC, Steven Schveighoffer wrote:
 try:

 pragma(msg, typeof(fun));
Outputs: ``` extern (C) void function(...) ```
OK, so it's a function pointer.
 
 But the compiler would complain if I call it as a type? And how could it 
 call it later without an address to it - this works.
You don't need an address to call it. The compiler shouldn't complain at all, nor should it decide to think you are calling it without parentheses.
 D should never call a function pointer without parentheses.
I'm very good in finding weird bugs ;-) Unfortunately, I cannot make an example. I already tried some changes/simplifications but cannot reproduce it outside the project.
I have encountered bugs like this (which disappear with seemingly unrelated removals). They are hard to deal with. Have you tried [dustmite](https://code.dlang.org/packages/dustmite)? It can reduce D code down automatically to a smaller example, but be warned that if you get the condition wrong, and your code base is huge, it can take hours/days to whittle down to something that isn't what you want. You can file a bug without a full example, just note that you have verified via the pragma(msg) statement above that the symbol is indeed a function pointer, and that compiler is trying to call it without explicit parentheses. However, without a complete example, it's going to be hard to find/fix.
 But how can I help the compiler to use it as correct type? On invocation 
 with arguments all works fine, but checking if the pointer is not null 
 fails. It not even works with an assignment. Could some `static if()` 
 maybe help or some asm trick?
This is a compiler bug, at least I think so. Since the compiler is misbehaving, it's not clear how to make it behave. -Steve
May 24 2022
parent reply frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 03:41:17 UTC, Steven Schveighoffer 
wrote:

 This is a compiler bug, at least I think so. Since the compiler 
 is misbehaving, it's not clear how to make it behave.

 -Steve
Well, ok, it's not my top priority and dustmite seems to run better on Unix - which I need to port anyway some time but not now. But it must be possible to check if the function pointer is valid in Assembler? But I'm not familiar with asm to do it correctly - I would be very appreciative if someone could post an example how to do it in D. So far, thank you for your time.
May 24 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/25/22 12:16 AM, frame wrote:
 On Wednesday, 25 May 2022 at 03:41:17 UTC, Steven Schveighoffer wrote:
 
 This is a compiler bug, at least I think so. Since the compiler is 
 misbehaving, it's not clear how to make it behave.

 -Steve
Well, ok, it's not my top priority and dustmite seems to run better on Unix - which I need to port anyway some time but not now. But it must be possible to check if the function pointer is valid in Assembler? But I'm not familiar with asm to do it correctly - I would be very appreciative if someone could post an example how to do it in D. So far, thank you for your time.
Well, you can possibly work around the type system, but I don't know what the compiler thinks you have there. ```d assert(*(cast(void**)&fun) !is null); ``` -Steve
May 24 2022
parent reply frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 04:34:43 UTC, Steven Schveighoffer 
wrote:

 Well, you can possibly work around the type system, but I don't 
 know what the compiler thinks you have there.
This really bothers me and I put this test cases: ```d static if (isSomeFunction!fun) pragma(msg,...) static if (isFunctionPointer!fun) pragma(msg,...) static if (isCallable!fun) pragma(msg,...) ``` It output mostly true except in one case where the compiler really sees a function, not a pointer. Thanks, now the difference is clear to me, Adam was right. I guess I have forgotten some version-switch somewhere and it tries to compile none-suitable code which will be never used in runtime at this point. This would have been more visible if the compiler just says: "function cannot be compared against null, only function pointer". That function vs function pointer is too subtle.
May 24 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/25/22 1:40 AM, frame wrote:

 This would have been more visible if the compiler just says: "function 
 cannot be compared against null, only function pointer". That function 
 vs function pointer is too subtle.
 
It's a case where the compiler can't divine what you were thinking when you wrote that code ;) remember that the expression `fun` where fun is a *function*, and not a *function pointer*, is the equivalent of `fun()`. It can't figure out that you really thought `fun` was a function pointer, maybe it thinks that you are missing an overload? Maybe fun actually is a no-arg function, and the result isn't comparable against null (or maybe it is, and it compiles and does something completely different!). But I'm glad you discovered the issue. -Steve
May 24 2022
parent reply frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 05:56:28 UTC, Steven Schveighoffer 
wrote:

 It's a case where the compiler can't divine what you were 
 thinking when you wrote that code ;)
I see not in all cases but in mine. If the compiler sees the function isn't callable without arguments and it is inside an if-statement or `assert()` then it could at least suggest a pointer or ask: are you dumb? ;-)
May 24 2022
parent reply user1234 <user1234 12.de> writes:
On Wednesday, 25 May 2022 at 06:04:10 UTC, frame wrote:
 On Wednesday, 25 May 2022 at 05:56:28 UTC, Steven Schveighoffer 
 wrote:

 It's a case where the compiler can't divine what you were 
 thinking when you wrote that code ;)
I see not in all cases but in mine. If the compiler sees the function isn't callable without arguments and it is inside an if-statement or `assert()` then it could at least suggest a pointer or ask: are you dumb? ;-)
As suggested by others, the reduction is not correct, you have stripped too muchbecause this compiles just fine: a.d: ```d alias F = void function(int); F f; static this() { assert(!f); assert(f is null); } ``` b.d: ```d import a; static this() { assert(!f); assert(f is null); } ``` ```sh $ dmd a.d b.d -main $ echo $? $ 0 ```
May 25 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/25/22 6:55 AM, user1234 wrote:
 On Wednesday, 25 May 2022 at 06:04:10 UTC, frame wrote:
 On Wednesday, 25 May 2022 at 05:56:28 UTC, Steven Schveighoffer wrote:

 It's a case where the compiler can't divine what you were thinking 
 when you wrote that code ;)
I see not in all cases but in mine. If the compiler sees the function isn't callable without arguments and it is inside an if-statement or `assert()` then it could at least suggest a pointer or ask: are you dumb? ;-)
As suggested by others, the reduction is not correct, you have stripped too muchbecause this compiles just fine:
Yes, he acknowledged that too much was stripped. I also verified similar code works. But the real problem was something else. He is saying in this message "why doesn't the compiler recognize that in comparing a function to null, I really wanted to compare a function *pointer* to null", but I don't see how the compiler can make that leap. Often times, I wish the compiler could just read what I was thinking when I wrote the code, so it could give me thought-contextual errors but alas, it can't. -Steve
May 25 2022
parent frame <frame86 live.com> writes:
On Wednesday, 25 May 2022 at 14:09:31 UTC, Steven Schveighoffer 
wrote:

 Yes, he acknowledged that too much was stripped. I also 
 verified similar code works.

 But the real problem was something else. He is saying in this 
 message "why doesn't the compiler recognize that in comparing a 
 function to null, I really wanted to compare a function 
 *pointer* to null", but I don't see how the compiler can make 
 that leap.

 Often times, I wish the compiler could just read what I was 
 thinking when I wrote the code, so it could give me 
 thought-contextual errors but alas, it can't.

 -Steve
The real problem was that I was locking my mind that the `fun` symbol was unique. In fact it was not. There was another code version where the `fun` symbol was declared as exported function and some import did read that code. My code was not affected from this bug since the call syntax for function and function pointer was the same. And I honestly never noticed that the compiler may says 'function' or 'function pointer'. It was always the same to me. I should have test it with `__traits(getLocation)` before.
May 25 2022
prev sibling parent Adam Ruppe <destructionator gmail.com> writes:
On Tuesday, 24 May 2022 at 18:54:33 UTC, frame wrote:
 Usually that works fine as shown in module A but for some 
 reason not in module B.
Do you have some other definition of fun somewhere?
May 24 2022