Iurii Selinnyi
Iurii Selinnyi

Reputation: 524

Dynamically construct function

I fear something like this is answered somewhere on this site, but I can't find it because I don't even know how to formulate the question. So here's the problem:

I have a voxel drowing function. First I calculate offsets, angles and stuff and after I do drowing. But I make few versions of every function because sometimes I want to copy pixel, sometimes blit, sometimes blit 3*3 square for every pixel for smoothing effect, sometimes just copy pixel to n*n pixels on the screen if object is resized. And there's tons of versions for that small part in the center of a function.

What can I do instead of writing 10 of same functions which differ only by central part of code? For performance reasons, passing a function pointer as an argument is not an option. I'm not sure making them inline will do the trick, because arguments I send differ: sometimes I calculate volume(Z value), sometimes I know pixels are drawn from bottom to top.

I assume there's some way of doing this stuff in C++ everybody knows about. Please tell me what I need to learn to do this. Thanks.

Upvotes: 4

Views: 271

Answers (5)

Useless
Useless

Reputation: 67852

The traditional OO approaches to this are the template method pattern and the strategy pattern.

Template Method

The first is an extension of the technique described in Vincenzo's answer: instead of writing a simple non-virtual wrapper, you write a non-virtual function containing the whole algorithm. Those parts that might vary, are virtual function calls. The specific arguments needed for a given implementation, are stored in the derived class object that provides that implementation.

eg.

class VoxelDrawer {
protected:
  virtual void copy(Coord from, Coord to) = 0;
  // any other functions you might want to change
public:
  virtual ~VoxelDrawer() {}
  void draw(arg) {
    for (;;) {
      // implement full algorithm
      copy(a,b);
    }
  }
};
class SmoothedVoxelDrawer: public VoxelDrawer {
  int radius; // algorithm-specific argument
  void copy(Coord from, Coord to) {
    blit(from.dx(-radius).dy(-radius),
         to.dx(-radius).dy(-radius),
         2*radius, 2*radius);
  }
public:
  SmoothedVoxelDrawer(int r) : radius(r) {}
};

Strategy

This is similar but instead of using inheritance, you pass a polymorphic Copier object as an argument to your function. Its more flexible in that it decouples your various copying strategies from the specific function, and you can re-use your copying strategies in other functions.

struct VoxelCopier {
  virtual void operator()(Coord from, Coord to) = 0;
};
struct SmoothedVoxelCopier: public VoxelCopier {
  // etc. as for SmoothedVoxelDrawer
};

void draw_voxels(arguments, VoxelCopier &copy) {
  for (;;) {
    // implement full algorithm
    copy(a,b);
  }
}

Although tidier than passing in a function pointer, neither the template method nor the strategy are likely to have better performance than just passing a function pointer: runtime polymorphism is still an indirect function call.

Policy

The modern C++ equivalent of the strategy pattern is the policy pattern. This simply replaces run-time polymorphism with compile-time polymorphism to avoid the indirect function call and enable inlining

// you don't need a common base class for policies,
// since templates use duck typing
struct SmoothedVoxelCopier {
  int radius;
  void copy(Coord from, Coord to) { ... }
};
template <typename CopyPolicy>
void draw_voxels(arguments, CopyPolicy cp) {
  for (;;) {
    // implement full algorithm
    cp.copy(a,b);
  }
}

Because of type deduction, you can simply call

draw_voxels(arguments, SmoothedVoxelCopier(radius));
draw_voxels(arguments, OtherVoxelCopier(whatever));

NB. I've been slightly inconsistent here: I used operator() to make my strategy call look like a regular function, but a normal method for my policy. So long as you choose one and stick with it, this is just a matter of taste.

CRTP Template Method

There's one final mechanism, which is the compile-time polymorphism version of the template method, and uses the Curiously Recurring Template Pattern.

template <typename Impl>
class VoxelDrawerBase {
protected:
  Impl& impl() { return *static_cast<Impl*>(this); }

  void copy(Coord from, Coord to) {...}
  // *optional* default implementation, is *not* virtual
public:
  void draw(arg) {
    for (;;) {
      // implement full algorithm
      impl().copy(a,b);
    }
  }
};
class SmoothedVoxelDrawer: public VoxelDrawerBase<SmoothedVoxelDrawer> {
  int radius; // algorithm-specific argument
  void copy(Coord from, Coord to) {
    blit(from.dx(-radius).dy(-radius),
         to.dx(-radius).dy(-radius),
         2*radius, 2*radius);
  }
public:
  SmoothedVoxelDrawer(int r) : radius(r) {}
};

Summary

In general I'd prefer the strategy/policy patterns for their lower coupling and better reuse, and choose the template method pattern only where the top-level algorithm you're parameterizing is genuinely set in stone (ie, when you're either refactoring existing code or are really sure of your analysis of the points of variation) and reuse is genuinely not an issue.

It's also really painful to use the template method if there is more than one axis of variation (that is, you have multiple methods like copy, and want to vary their implementations independently). You either end up with code duplication or mixin inheritance.

Upvotes: 6

Ivan Mushketyk
Ivan Mushketyk

Reputation: 8305

You can use either Template Method pattern or Strategy pattern. Usually Template method pattern is used in white-box frameworks, when you need to know about the internal structure of a framework to correctly subclass a class. Strategy pattern is usually used in black-box frameworks, when you should not know about the implementation of the framework, since you only need to understand the contract of the methods you should implement.

For performance reasons, passing a function pointer as an argument is not an option.

Are you sure that passing one additional parameter and will cause performance problems? In this case you may have similar performance penalties if you use OOP techniques, like Template method or Strategy. But it is usually necessary to use profilier to determine what is the source of the performance degradation. Virtual calls, passing additional parameters, calling function through a pointer are usually very cheap, comparing to complex algorithms. You may find that these techniques consumes insignificant percent of CPU resources comparing to other code.

I'm not sure making them inline will do the trick, because arguments I send differ: sometimes I calculate volume(Z value), sometimes I know pixels are drawn from bottom to top.

You could pass all the parameter required for drawing in all cases. Alternatively if use Tempate method pattern a base class could provide methods that can return the data that could be required for drawing in different cases. In Strategy pattern, you could pass an instance of an object that could provide this kind of data to a Strategy implementation.

Upvotes: 0

manasij7479
manasij7479

Reputation: 1705

You could use higher order functions, if your 'central part' can be parameterized nicely.
Here is a simple example of a function that returns a function which adds n to its argument:

#include <iostream>
#include<functional>
std::function<int(int)> n_adder(int n)
{
    return [=](int x){return x+n;};
}
int main()
{
    auto add_one = n_adder(1);
    std::cout<<add_one(5);
}

Upvotes: 0

doctorlove
doctorlove

Reputation: 19317

You could simply use the strategy pattern

So, instead of something like

void do_something_one_way(...)
{
    //blah
    //blah
    //blah
    one_way();
    //blah
    //blah
}

void do_something_another_way(...)
{
    //blah
    //blah
    //blah
    another_way();
    //blah
    //blah
}

You will have

void do_something(...)
{
    //blah
    //blah
    //blah
    any_which_way();
    //blah
    //blah
}

any_which_way could be a lambda, a functor, a virtual member function of a strategy class passed in. There are many options.

Are you sure that

"passing a function pointer as an argument is not an option"

Does it really slow it down?

Upvotes: 1

Vincenzo Pii
Vincenzo Pii

Reputation: 19855

I suggest using the NVI idiom.

You have your public method which calls a private function that implements the logic that must differ from case to case.

Derived classes will have to provide an implementation of that private function that specializes them for their particular task.

Example:

class A {
public:
    void do_base() {
        // [pre]
        specialized_do();
        // [post]
    }

private:
    virtual void specialized_do() = 0;
};

class B : public A {
private:
    void specialized_do() {
        // [implementation]
    }
};

The advantage is that you can keep a common implementation in the base class and detail it as required for any subclass (which just need to reimplement the specialized_do method).

The disadvantage is that you need a different type for each implementation, but if your use case is drawing different UI elements, this is the way to go.

Upvotes: 3

Related Questions