Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uses of pointers non-type template parameters?

Tags:

c++

templates

Has anyone ever used pointers/references/pointer-to-member (non-type) template parameters?
I'm not aware of any (sane/real-world) scenario in which that C++ feature should be used as a best-practice.

Demonstation of the feature (for pointers):

template <int* Pointer> struct SomeStruct {};
int someGlobal = 5;
SomeStruct<&someGlobal> someStruct; // legal c++ code, what's the use?

Any enlightenment will be much appreciated!

like image 878
infokiller Avatar asked Nov 04 '12 16:11

infokiller


3 Answers

Pointer-to-function:

Pointer-to-member-function and pointer-to-function non-type parameters are really useful for some delegates. It allows you to make really fast delegates.

Ex:

#include <iostream>
struct CallIntDelegate
{
    virtual void operator()(int i) const = 0;
};

template<typename O, void (O::*func)(int)>
struct IntCaller : public CallIntDelegate
{
    IntCaller(O* obj) : object(obj) {}
    void operator()(int i) const
    {
        // This line can easily optimized by the compiler
        // in object->func(i) (= normal function call, not pointer-to-member call)
        // Pointer-to-member calls are slower than regular function calls
        (object->*func)(i);
    }
private:
    O* object;
};

void set(const CallIntDelegate& setValue)
{
    setValue(42);
}

class test
{
public:
    void printAnswer(int i)
    {
        std::cout << "The answer is " << 2 * i << "\n";
    }
};

int main()
{
    test obj;
    set(IntCaller<test,&test::printAnswer>(&obj));
}

Live example here.

Pointer-to-data:

You can use such non-type parameters to extend the visibility of a variable.

For example, if you were coding a reflexion library (which might very useful for scripting), using a macro to let the user declare his classes for the library, you might want to store all data in a complex structure (which may change over time), and want some handle to use it.

Example:

#include <iostream>
#include <memory>

struct complex_struct
{
    void (*doSmth)();
};

struct complex_struct_handle
{
    // functions
    virtual void doSmth() = 0;
};

template<complex_struct* S>
struct csh_imp : public complex_struct_handle
{
    // implement function using S
    void doSmth()
    {
        // Optimization: simple pointer-to-member call,
        // instead of:
        // retrieve pointer-to-member, then call it.
        // And I think it can even be more optimized by the compiler.
        S->doSmth();
    }
};

class test
{
    public:
        /* This function is generated by some macros
           The static variable is not made at class scope
           because the initialization of static class variables
           have to be done at namespace scope.

           IE:
               class blah
               {
                   SOME_MACRO(params)
               };
           instead of:
               class blah
               {
                   SOME_MACRO1(params)
               };
               SOME_MACRO2(blah,other_params);

           The pointer-to-data template parameter allows the variable
           to be used outside of the function.
        */
        std::auto_ptr<complex_struct_handle> getHandle() const
        {
            static complex_struct myStruct = { &test::print };
            return std::auto_ptr<complex_struct_handle>(new csh_imp<&myStruct>());
        }
        static void print()
        {
            std::cout << "print 42!\n";
        }
};

int main()
{
    test obj;
    obj.getHandle()->doSmth();
}

Sorry for the auto_ptr, shared_ptr is available neither on Codepad nor Ideone. Live example.

like image 136
Synxis Avatar answered Nov 15 '22 19:11

Synxis


The case for a pointer to member is substantially different from pointers to data or references.

Pointer to members as template parameters can be useful if you want to specify a member function to call (or a data member to access) but you don't want to put the objects in a specific hierarchy (otherwise a virtual method is normally enough).

For example:

#include <stdio.h>

struct Button
{
    virtual ~Button() {}
    virtual void click() = 0;
};

template<class Receiver, void (Receiver::*action)()>
struct GuiButton : Button
{
    Receiver *receiver;
    GuiButton(Receiver *receiver) : receiver(receiver) { }
    void click() { (receiver->*action)(); }
};

// Note that Foo knows nothing about the gui library    
struct Foo
{
    void Action1() { puts("Action 1\n"); }
};

int main()
{
    Foo foo;
    Button *btn = new GuiButton<Foo, &Foo::Action1>(&foo);
    btn->click();
    return 0;
}

Pointers or references to global objects can be useful if you don't want to pay an extra runtime price for the access because the template instantiation will access the specified object using a constant (load-time resolved) address and not an indirect access like it would happen using a regular pointer or reference. The price to pay is however a new template instantiation for each object and indeed it's hard to think to a real world case in which this could be useful.

like image 39
6502 Avatar answered Nov 15 '22 21:11

6502


The Performance TR has a few example where non-type templates are used to abstract how the hardware is accessed (the hardware stuff starts at page 90; uses of pointers as template arguments are, e.g., on page 113). For example, memory mapped I/O registered would use a fixed pointer to the hardware area. Although I haven't ever used it myself (I only showed Jan Kristofferson how to do it) I'm pretty sure that it is used for development of some embedded devices.

like image 4
Dietmar Kühl Avatar answered Nov 15 '22 21:11

Dietmar Kühl