3

One of Objective C's primary features is simple introspection. A typical use of this functionality is the ability to check some method (function), to make sure it indeed exists, before calling it.

Whereas the following code will throw an error at runtime (although it compiles just fine (Apple LLVM version 7.0.2 (clang-700.1.81)))...

@import         Foundation;
@interface      Maybe : NSObject + (void) maybeNot; @end
@implementation Maybe                               @end

int main (){ [Maybe maybeNot]; }

By adding one simple condition before the call...

if ([Maybe respondsToSelector:@selector(maybeNot)])

We can wait till runtime to decide whether or not to call the method.

Is there any way to do this with "standard" C (c11) or C++ (std=c14)?

i.e....

extern void callMeIfYouDare();

int main() { /* if (...) */ callMeIfYouDare(); }

I guess I should also mention that I am testing/using this is in a Darwin runtime environment.

Alex Gray
  • 16,007
  • 9
  • 96
  • 118

3 Answers3

4

On GNU gcc / Mingw32 / Cygwin you can use Weak symbol:

#include <stdio.h>

extern void __attribute__((weak)) callMeIfYouDare();

void (*callMePtr)() = &callMeIfYouDare;

int main() {
        if (callMePtr) {
                printf("Calling...\n");
                callMePtr();
        } else {
                printf("callMeIfYouDare() unresolved\n");
        }
}

Compile and run:

$ g++ test_undef.cpp -o test_undef.exe

$ ./test_undef.exe
callMeIfYouDare() unresolved

If you link it with library that defines callMeIfYouDare though it will call it. Note that going via the pointer is necessary in Mingw32/Cygwin at least. Placing a direct call callMeIfYouDare() will result in a truncated relocation by default which unless you want to play with linker scripts is unavoidable.

Using Visual Studio, you might be able to get __declspec(selectany) to do the same trick: GCC style weak linking in Visual Studio?

Update #1: For XCode you can use __attribute__((weak_import)) instead according to: Frameworks and Weak Linking

Update #2: For XCode based on "Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)" I managed to resolve the issue by compiling with the following command:

g++ test_undef.cpp -undefined dynamic_lookup -o test_undef

and leaving __attribute__((weak)) as it is for the other platforms.

Community
  • 1
  • 1
ALGOholic
  • 644
  • 3
  • 7
  • That's a "no go" with `Xcode 7.x`'s bundled tools. Im instilling the latest `gcc` to see if that does any better (for my platform)... – Alex Gray Feb 16 '16 at 07:02
  • What's the Xcode saying? Check this out: https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html Apparently instead of __attribute__((weak)) you can use __attribute__((weak_import)) – ALGOholic Feb 16 '16 at 07:02
  • 1
    Even Apple's own example won't compile, _for me_. A complete, non-functional example project, [can be found here](https://github.com/mralexgray/WeakLinks). – Alex Gray Feb 16 '16 at 07:20
  • I'll try this on my mac in the evening. – ALGOholic Feb 16 '16 at 07:25
  • 1
    Ok, I tested it on my Mac and seemd to have solve the issue by compiling with the following command: `g++ test_undef.cpp -undefined dynamic_lookup -o test_undef` and leaving `__attribute__((weak))` as it is. Furthermore - the pointer trick is not necessary with my version of XCode - callMeIfYouDare() can be checked and called directly. Please confirm if it works for you. – ALGOholic Feb 16 '16 at 17:15
  • YES, that _does seem to work_. More [info here](http://stackoverflow.com/questions/3695234/what-is-the-deal-with-undefined-symbols-in-a-shared-library-or-dylib), but basically, you need the (`Xcode`) `OTHER_LDFLAGS = -undefined dynamic_lookup`, at which point (even) Apple's code works. [Project updated](https://github.com/mralexgray/WeakLinks) to reflect this solution. – Alex Gray Feb 16 '16 at 19:34
  • What else would you like to know to accept this as an answer? – ALGOholic Feb 17 '16 at 06:19
1

C++ or C don't have introspection. You could add some with your additional layer (look at Qt metaobject, or GTK GObject introspection for examples); you might consider customizing GCC with MELT to get some introspection... (but that would take weeks). You could have some additional script or tool which emits C or C++ code related to your introspection needs (SWIG could be inspirational).

In your particular case, you might want to use weak symbols (at least on Linux). Perhaps use the relevant function attribute so code.

extern void perhapshere(void) __attribute__((weak));
if (perhapshere) 
   perhapshere();

and you might even make that shorter with some macro.

Maybe you just want to load some plugin with dlopen(3) and use dlsym(3) to find symbols in it (or even in the whole program which you would link with -rdynamic, by giving the NULL path to dlopen and using dlsym on the obtained handle); be aware that C++ uses name mangling.

So you might try

void*mainhdl = dlopen(NULL, RTLD_NOW);
if (!mainhdl) { fprintf(stderr, "dlopen failed %s\n", dlerror());
                exit(EXIT_FAILURE); };

then later:

typedef void voidvoidsig_t (void); // the signature of perhapshere
void* ad = dlsym(mainhdl, "perhapshere"); 
if (ad != NULL) {
   voidvoidsig_t* funptr = (voidvoidsig_t*)ad;
   (*funptr)();
}
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I still get `Undefined symbols for architecture x86_64` - even after declaring the function `weak` This is originally how I thought it would be done, just don't quite know why it doesn't work. – Alex Gray Feb 16 '16 at 06:36
  • Then show your exact code in the question and explain how you build your program. Last time I used the weak symbol trick it did work on Linux. – Basile Starynkevitch Feb 16 '16 at 06:38
  • nothing fancy... code is verbatim as above, compiled with `clang src -o exe` – Alex Gray Feb 16 '16 at 06:45
  • This could be OS specific. You really should **mention your *exact* code** in *your question*, what OS are you using, **how did you link your program**, etc. – Basile Starynkevitch Feb 16 '16 at 06:46
1

If you can see a function of an object (not pointer) is called in a source code and the code is compiled successfully - then the function does exist and no checking needed.

If a function being called via a pointer then you assume your pointer is of type of the class that has that function. To check whether it's so or not you use casting:

auto* p = dynamic_cast<YourClass*>(somepointer);
if (p != nullptr)
  p->execute();
Andrey Lyubimov
  • 663
  • 6
  • 22