Reputation: 14408
Certain situations in my code, I end up invoking the function only if that function is defined, or else I should not. How can I achieve this?
like:
if (function 'sum' exists ) then invoke sum ()
Maybe the other way around to ask this question is how to determine if function is defined at runtime and if so, then invoke?
Upvotes: 46
Views: 80434
Reputation: 321
Here's another method: Declare optional methods as virtual methods within a struct, then use that struct as a base class for derived objects that implement the method.
Example (including the outline of a use case):
#include <map> // For std::map
#include <string> // For std::string
#include <stdio.h> // For printf
//----------------------------------------------------------------------------
class Basic {
public:
typedef std::map<std::string, Basic*> Map_t; // The Map type
static Map_t map; // The Global map
Basic(std::string name)
{ map[name]= this; }
virtual
~Basic( void )
{ }
// Optional methods- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct has_stop {
virtual void
stop( void )
{ }
}; // struct has_stop
struct has_wait {
virtual void
wait( void )
{ }
}; // struct has_wait
}; // class Basic
Basic::Map_t Basic::map;
//----------------------------------------------------------------------------
class Global {
public:
void
stop( void )
{
Basic::Map_t& map= Basic::map;
for(auto const& mi: map) {
Basic::has_stop* method= dynamic_cast<Basic::has_stop*>(mi.second);
if( method )
method->stop();
else
printf("Basic(%s) doesn't have stop()\n", mi.first.c_str());
}
}
void
wait( void )
{
Basic::Map_t& map= Basic::map;
for(auto const& mi: map) {
Basic::has_wait* method= dynamic_cast<Basic::has_wait*>(mi.second);
if( method )
method->wait();
else
printf("Basic(%s) doesn't have wait()\n", mi.first.c_str());
}
}
}; // class Global
static Global global;
//----------------------------------------------------------------------------
class One : public Basic {
public:
One(std::string name)
: Basic(name) {}
// While the stop method is present, has_stop is not a base class.
// Global::stop won't find the method.
virtual void stop( void )
{ printf("One::stop\n"); }
}; // class One
//----------------------------------------------------------------------------
class Two
: public Basic
, public Basic::has_stop, public Basic::has_wait {
public:
Two(std::string name)
: Basic(name) {}
virtual void stop( void ) // (Overrides Basic::has_stop)
{ printf("Two::stop\n"); }
virtual void wait( void ) // (Overrides Basic::has_wait)
{ printf("Two::wait\n"); }
}; // class Two
//----------------------------------------------------------------------------
int main( void ) {
One one("one");
Two two("two");
one.stop(); // We can invoke One::stop()
global.stop(); // But Global::stop won't find it
global.wait();
return 0;
} // main
Output:
One::stop
Basic(one) doesn't have stop()
Two::stop
Basic(one) doesn't have wait()
Two::wait
Another (simpler) option is to always include the functions in the base class, with the base class implementation empty except for returning whatever the function signature requires.
Upvotes: 0
Reputation: 501
In c you can use array of function pointers
#include<stdio.h>
#include<string.h>
typedef struct i32_2arg{int a, b;}i32_2_arg;
typedef struct i32_arg{int a;}i32_arg;
void add(void*a,void*r){
i32_2_arg* l = (i32_2_arg*)a;
((i32_arg*)r)->a = l->a+l->b;
}
void sub(void*a,void*r){}
char lFunNames[8][64] = {"add","sub",0};
void (*lFuns[8]) (void*,void*) = {&add,&sub,0};
void evalfun(char* lName, void* inargs,void* outargs){
for(int i = 0; i < 8; i++ )
if (!strcmp(lFunNames[i],lName)) (*lFuns[i])(inargs,outargs);
}
int main(){
i32_2_arg ab ={2,3};
i32_arg sum;
evalfun("add",&ab,&sum);
printf("if \"add\" exists, result is %d\n",sum.a);
return 0;
}
Upvotes: 1
Reputation: 432
Using GCC you can:
void func(int argc, char *argv[]) __attribute__((weak)); // weak declaration must always be present
// optional definition:
/*void func(int argc, char *argv[]) {
printf("FOUND THE FUNCTION\n");
for(int aa = 0; aa < argc; aa++){
printf("arg %d = %s \n", aa, argv[aa]);
}
}*/
int main(int argc, char *argv[]) {
if (func){
func(argc, argv);
} else {
printf("did not find the function\n");
}
}
If you uncomment func it will run it otherwise it will print "did not find the function\n".
Upvotes: 29
Reputation: 7715
When you declare 'sum' you could declare it like:
#define SUM_EXISTS
int sum(std::vector<int>& addMeUp) {
...
}
Then when you come to use it you could go:
#ifdef SUM_EXISTS
int result = sum(x);
...
#endif
I'm guessing you're coming from a scripting language where things are all done at runtime. The main thing to remember with C++ is the two phases:
So all the #define
and things like that happen at compile time.
....
If you really wanted to do it all at runtime .. you might be interested in using some of the component architecture products out there.
Or maybe a plugin kind of architecture is what you're after.
Upvotes: 21
Reputation: 8475
This answer is for global functions, as a complement to the other answers on testing methods. This answer only applies to global functions.
First, provide a fallback dummy function in a separate namespace. Then determine the return type of the function-call, inside a template parameter. According to the return-type, determine if this is the fallback function or the wanted function.
If you are forbidden to add anything in the namespace of the function, such as the case for std::
, then you should use ADL to find the right function in the test.
For example, std::reduce()
is part of c++17, but early gcc compilers, which should support c++17, don't define std::reduce()
. The following code can detect at compile-time whether or not std::reduce
is declared. See it work correctly in both cases, in compile explorer.
#include <numeric>
namespace fallback
{
// fallback
std::false_type reduce(...) { return {}; }
// Depending on
// std::recuce(Iter from, Iter to) -> decltype(*from)
// we know that a call to std::reduce(T*, T*) returns T
template <typename T, typename Ret = decltype(reduce(std::declval<T*>(), std::declval<T*>()))>
using return_of_reduce = Ret;
// Note that due to ADL, std::reduce is called although we don't explicitly call std::reduce().
// This is critical, since we are not allowed to define any of the above inside std::
}
using has_reduce = fallback::return_of_reduce<std::true_type>;
// using has_sum = std::conditional_t<std::is_same_v<fallback::return_of_sum<std::true_type>,
// std::false_type>,
// std::false_type,
// std::true_type>;
#include <iterator>
int main()
{
if constexpr (has_reduce::value)
{
// must have those, so that the compile will find the fallback
// function if the correct one is undefined (even if it never
// generates this code).
using namespace std;
using namespace fallback;
int values[] = {1,2,3};
return reduce(std::begin(values), std::end(values));
}
return -1;
}
In cases, unlike the above example, when you can't control the return-type, you can use other methods, such as std::is_same
and std::contitional
.
For example, assume you want to test if function int sum(int, int)
is declared in the current compilation unit. Create, in a similar fashion, test_sum_ns::return_of_sum
. If the function exists, it will be int
and std::false_type
otherwise (or any other special type you like).
using has_sum = std::conditional_t<std::is_same_v<test_sum_ns::return_of_sum,
std::false_type>,
std::false_type,
std::true_type>;
Then you can use that type:
if constexpr (has_sum::value)
{
int result;
{
using namespace fallback; // limit this only to the call, if possible.
result = sum(1,2);
}
std::cout << "sum(1,2) = " << result << '\n';
}
NOTE: You must have to have using namespace
, otherwise the compiler will not find the fallback function inside the if constexpr
and will complain. In general, you should avoid using namespace
since future changes in the symbols inside the namespace may break your code. In this case there is no other way around it, so at least limit it to the smallest scope possible, as in the above example
Upvotes: 0
Reputation: 21
In C++, a modified version of the trick for checking if a member exists should give you what you're looking for, at compile time instead of runtime:
#include <iostream>
#include <type_traits>
namespace
{
template <class T, template <class...> class Test>
struct exists
{
template<class U>
static std::true_type check(Test<U>*);
template<class U>
static std::false_type check(...);
static constexpr bool value = decltype(check<T>(0))::value;
};
template<class U, class = decltype(sum(std::declval<U>(), std::declval<U>()))>
struct sum_test{};
template <class T>
void validate_sum()
{
if constexpr (exists<T, sum_test>::value)
{
std::cout << "sum exists for type " << typeid(T).name() << '\n';
}
else
{
std::cout << "sum does not exist for type " << typeid(T).name() << '\n';
}
}
class A {};
class B {};
void sum(const A& l, const A& r); // we only need to declare the function, not define it
}
int main(int, const char**)
{
validate_sum<A>();
validate_sum<B>();
}
Here's the output using clang:
sum exists for type N12_GLOBAL__N_11AE
sum does not exist for type N12_GLOBAL__N_11BE
I should point out that weird things happened when I used an int instead of A (sum()
has to be declared before sum_test
for the exists
to work, so maybe exists
isn't the right name for this). Some kind of template expansion that didn't seem to cause problems when I used A. Gonna guess it's ADL-related.
Upvotes: 2
Reputation: 1
While other replies are helpful advices (dlsym
, function pointers, ...), you cannot compile C++ code referring to a function which does not exist. At minimum, the function has to be declared; if it is not, your code won't compile. If nothing (a compilation unit, some object file, some library) defines the function, the linker would complain (unless it is weak, see below).
But you should really explain why you are asking that. I can't guess, and there is some way to achieve your unstated goal.
Notice that dlsym
often requires functions without name mangling, i.e. declared as extern "C"
.
If coding on Linux with GCC, you might also use the weak
function attribute in declarations. The linker would then set undefined weak symbols to null.
If you are getting the function name from some input, you should be aware that only a subset of functions should be callable that way (if you call an arbitrary function without care, it will crash!) and you'll better explicitly construct that subset. You could then use a std::map
, or dlsym
(with each function in the subset declared extern "C"
). Notice that dlopen
with a NULL
path gives a handle to the main program, which you should link with -rdynamic
to have it work correctly.
You really want to call by their name only a suitably defined subset of functions. For instance, you probably don't want to call this way abort
, exit
, or fork
.
NB. If you know dynamically the signature of the called function, you might want to use libffi to call it.
Upvotes: 19
Reputation: 4722
You can use #pragma weak
for the compilers that support it (see the weak symbol wikipedia entry).
This example and comment is from The Inside Story on Shared Libraries and Dynamic Loading:
#pragma weak debug
extern void debug(void);
void (*debugfunc)(void) = debug;
int main() {
printf(“Hello World\n”);
if (debugfunc) (*debugfunc)();
}
you can use the weak pragma to force the linker to ignore unresolved symbols [..] the program compiles and links whether or not debug() is actually defined in any object file. When the symbol remains undefined, the linker usually replaces its value with 0. So, this technique can be a useful way for a program to invoke optional code that does not require recompiling the entire application.
Upvotes: 5
Reputation: 7715
So another way, if you're using c++11 would be to use functors:
You'll need to put this at the start of your file:
#include <functional>
The type of a functor is declared in this format:
std::function< return_type (param1_type, param2_type) >
You could add a variable that holds a functor for sum like this:
std::function<int(const std::vector<int>&)> sum;
To make things easy, let shorten the param type:
using Numbers = const std::vectorn<int>&;
Then you could fill in the functor var with any one of:
A lambda:
sum = [](Numbers x) { return std::accumulate(x.cbegin(), x.cend(), 0); } // std::accumulate comes from #include <numeric>
A function pointer:
int myFunc(Numbers nums) {
int result = 0;
for (int i : nums)
result += i;
return result;
}
sum = &myFunc;
Something that 'bind' has created:
struct Adder {
int startNumber = 6;
int doAdding(Numbers nums) {
int result = 0;
for (int i : nums)
result += i;
return result;
}
};
...
Adder myAdder{2}; // Make an adder that starts at two
sum = std::bind(&Adder::doAdding, myAdder);
Then finally to use it, it's a simple if statement:
if (sum)
return sum(x);
In summary, functors are the new pointer to a function, however they're more versatile. May actually be inlined if the compiler is sure enough, but generally are the same as a function pointer.
When combined with std::bind and lambda's they're quite superior to old style C function pointers.
But remember they work in c++11 and above environments. (Not in C or C++03).
Upvotes: 4
Reputation: 1496
I suspect that the poster was actually looking for something more along the lines of SFINAE checking/dispatch. With C++ templates, can define to template functions, one which calls the desired function (if it exists) and one that does nothing (if the function does not exist). You can then make the first template depend on the desired function, such that the template is ill-formed when the function does not exist. This is valid because in C++ template substitution failure is not an error (SFINAE), so the compiler will just fall back to the second case (which for instance could do nothing).
See here for an excellent example: Is it possible to write a template to check for a function's existence?
Upvotes: 16
Reputation: 22125
If you know what library the function you'd like to call is in, then you can use dlsym()
and dlerror()
to find out whether or not it's there, and what the pointer to the function is.
Edit: I probably wouldn't actually use this approach - instead I would recommend Matiu's solution, as I think it's much better practice. However, dlsym()
isn't very well known, so I thought I'd point it out.
Upvotes: 5
Reputation: 1989
use pointers to functions.
//initialize
typedef void (*PF)();
std::map<std::string, PF> defined_functions;
defined_functions["foo"]=&foo;
defined_functions["bar"]=&bar;
//if defined, invoke it
if(defined_functions.find("foo") != defined_functions.end())
{
defined_functions["foo"]();
}
Upvotes: 10