0

What is the best way to declare global instances of a function object so I can import and use the instances as callables throughout my program?

Specifically, I have created a template class which serves as a custom deleter for a shared pointer. Several pointer types in a third party library need to be deleted with a "Free" function that takes a reference to the pointer. An instance of the class is instantiated with the type of the pointer to delete, and a pointer to a function with the signature of the Free function. I am declaring the instances as const because there is no need for the member function pointer to ever change.

template <class T>
class Deleter {
public:
  typedef typename bool(CALLING_CONVENTION *DeleterFunc)(T**);
  Deleter(DeleterFunc deleter) : deleter_(deleter) {}
  void operator() (T* t) { if (t) { deleter_(&t) }

private:
  DeleterFunc deleter_;
};

static const Deleter<I_x> x_deleter(FreeXInterface);

My first attempt was a create instances for each of the pointer types in the .h file, but this resulted in multiply defined symbols if I include this header file in other code. So I changed the declaration of the instances to "static" and this compiles and seems to work fine, but I have seen warnings that this is not a good idea (especially if the objects belong to a namespace) because static is causing the linkage to be file-only, and that each compilation unit will have its own copy.

My question is does this matter if I don't really care if they are the same instance between files? I am not using this as global data, and the function objects don't really have any state. Would I have any concerns about threading if these objects are declared static? Is there a better way to implement this without using the static keyword?

Paul
  • 189
  • 1
  • 6
  • If you don't care if they're the same instance, why not simply hold the functor in the shared pointer, and make a new one each time? – Sneftel Sep 24 '15 at 06:43
  • I intend to create many different shared_ptr(I_x* x_raw, x_deleter) in many different scopes around my program, I only want to reuse x_deleter rather than having clients have to create instances themselves (and have to import the raw pointer type dependencies). – Paul Sep 24 '15 at 06:53
  • If the users are fine with `shared_ptr(x_raw, x_deleter)`, why not `shared_ptr(x_raw, X_Deleter())`? – Sneftel Sep 24 '15 at 08:57
  • It would have to be `shared_ptr(x_raw, X_Deleter(FreeXInterface)` which seems like a burden, especially having to know the proper Free function which I would prefer to hide. Unless I'm missing something in what you are suggesting. – Paul Sep 24 '15 at 16:14
  • Ah, I see what you're saying. I'll answer at length. – Sneftel Sep 24 '15 at 16:32
  • I think that is on the right track, though; I DO want something that is more like a class or a function rather than an instance, essentially having the classes or functions in the header file where the FreeXInterface function is already bound and then having the clients able to create an instance or passing in the function. I am just not exactly sure how to accomplish that. – Paul Sep 24 '15 at 17:10

1 Answers1

0

Well, the basic answer to your linker question is here: How do I use extern to share variables between source files? Let's try to do a little better, though: You currently have to refer to the deletion function every time you instantiate a function, instead of having it chosen automatically based on the type.

Of course we often pick types based on types: std::vector<int> is the dynamic array which is capable of holding ints. Linkage isn't a huge problem there: Everything's more or less inlined, each translation unit which mentions std::vector<int>::push_back() gets its own copy of the object code to put an int into a vector of ints, and the linker (sometimes) helps out by removing duplicated instantiations. But here we want an object, not a type.

So what's halfway between templates and global objects? Static members of templated classes! Check it:

// Deleter.h

typedef typename bool(CALLING_CONVENTION *DeleterFunc)(T**);

template <class T>
class Deleter {
public:
    static DeleterFunc s_deleterFunc;

    void operator() (T* t) {
        if(t) { s_deleterFunc(&t); }
    }
};

 

// XDeleter.cpp

#include "Deleter.h"
template<>
DeleterFunc Deleter<I_x>::s_deleterFunc = FreeXInterface;

 

// YDeleter.cpp

#include "Deleter.h"
template<>
DeleterFunc Deleter<I_y>::s_deleterFunc = FreeYInterface;

For each type you want a deleter for, you just provide the instantiation of the static member for the specialized class. The code which calls Deleter<I_x>::operator() only needs to include Deleter.h; the linker will take care of matching it to the one declared in XDeleter.cpp.

Note that I'm doing it with templated classes, rather than templated functions, only because it allows you to use your function pointers. But you could instead do something as simple as:

// Deleter.h

template <class T>
invokeDeleter(T* t);

 

// XDeleter.cpp

#include "Deleter.h"
template<>
invokeDeleter<I_x>(I_x* x)
{
    ...
}

 

// YDeleter.cpp

#include "Deleter.h"
template<>
invokeDeleter<I_y>(I_y* y)
{
    ...
}
Community
  • 1
  • 1
Sneftel
  • 40,271
  • 12
  • 71
  • 104