Original Post
I've been working on converting (more like rewriting at this point with how different it is) a noise/fractal library I wrote in C# some time ago, and have a module like this:
If its not immediately obvious, its a module that basically takes another module and a lambda function, and when executed (call to get(x,y,z) ), passes the output of the module into the lambda function. A simple use case looks something like this:
Where get(0,0,0) for example would return 0 from the perlin function (always zero at integral inputs) and therefore 1 from the compose function calling cos().
It's a fairly typical infrastructure for noise libraries, except generalized a bit from the use of Lambdas. It's worked for me fairlly well, but I'd like to be able to extend it to operate on multiple modules at once. For example, a common operation is something like "blend", which takes two modules and lerps between their outputs by the output of a third module (ideally clamped to the range of [0,1]). It would be nice to be able to extend my existing functionality to call something like this:
Unless there's some kind of crazy magic-bullet third option, I can see two ways to implement this sort of extension:
//interface
class Compose : public IModule
{
public:
Compose(void);
~Compose(void);
double get(double,double,double,double);
double get(double,double,double);
double get(double,double);
Compose* setInput(IModule*);
Compose* setWrapper(std::function<double (double) >);
private:
std::function<double (double) > f_;
std::shared_ptr<IModule> basis_;
};
//implementation
Compose* Compose::setWrapper(std::function<double (double) > function)
{
f_ = function;
return this;
}
Compose* Compose::setInput(IModule* module)
{
basis_ = std::shared_ptr<IModule>(module);
return this;
}
double Compose::get(double x,double y,double z,double w)
{
return f_(basis_->get(x,y,z,w));
}
double Compose::get(double x,double y,double z)
{
return f_(basis_->get(x,y,z));
}
double Compose::get(double x,double y)
{
return f_(basis_->get(x,y));
}
If its not immediately obvious, its a module that basically takes another module and a lambda function, and when executed (call to get(x,y,z) ), passes the output of the module into the lambda function. A simple use case looks something like this:
Compose* sample = new Compose;
sample->setWrapper([](double n){ return std::cos(n); })->setInput(new Perlin);
Where get(0,0,0) for example would return 0 from the perlin function (always zero at integral inputs) and therefore 1 from the compose function calling cos().
It's a fairly typical infrastructure for noise libraries, except generalized a bit from the use of Lambdas. It's worked for me fairlly well, but I'd like to be able to extend it to operate on multiple modules at once. For example, a common operation is something like "blend", which takes two modules and lerps between their outputs by the output of a third module (ideally clamped to the range of [0,1]). It would be nice to be able to extend my existing functionality to call something like this:
//NOT REAL CODE
auto blend = [](double a, double b, double selector){ return a + selector*(b-a); };
Compose* sample = new Compose;
sample->setInput(new Perlin)->addInput(new Perlin)->addInput(new Perlin)->setWrapper(blend);
Unless there's some kind of crazy magic-bullet third option, I can see two ways to implement this sort of extension:
- Make the lambda accept a vector
. This lets me pass totally arbitrary functions (which is kinda nice), but makes the syntax for declaring the lambdas all kinds of weird and causes some minor bounds checking issues that can probably be worked out
- create overloads up to n (the number of arguments I care about. For example:
this has the advantage of being a little safer in terms of bounds checking and has cleaner lambda syntax, but isn't as straightforward to use and can't extend very far.
//interface snippet
Compose* setWrapper(std::function<double (double) >);
Compose* setWrapper(std::function<double (double,double) >);
Compose* setWrapper(std::function<double (double,double,double) >);
Compose* setWrapper(std::function<double (double,double,double,double) >);
private:
std::function<double (double) > f1_; //versus std::function<double(std::vector<double>)> f_;
std::function<double (double,double) > f2_;
std::function<double (double,double) > f3_;
std::function<double (double,double) > f4_;
std::vector<std::shared_ptr<IModule> > basis_; //has to be a vector in either implementation
};
//implementation snippet
double Compose::get(double x,double y)
{
if(f4)
return f4_(basis[0]_->get(x,y), basis[1]_->get(x,y), basis[2]_->get(x,y), basis[3]_->get(x,y));
else if(f3)
//...continued
}
Either approach is fine for my own personal needs, but I eventually want to release this as a library, so from that perspective, which approach (unless you know of a better one -- I'm all ears) would be ideal?
EDIT: Looks like the code box axed my whitespace. I think it's pretty easy code to read overall anyway, but that's unfair to say since it's my code. Sorry if that causes any issues. - create overloads up to n (the number of arguments I care about. For example: