LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   C++: different instances of a function template (https://www.linuxquestions.org/questions/programming-9/c-different-instances-of-a-function-template-702171/)

unihiekka 02-04-2009 03:34 AM

C++: different instances of a function template
 
Hi!

In my class template I have implemented several numerical integrators of different orders, i.e. I have a set of basic methods (M_1, ... M_N, say, where N > 1) that integrate a set of equations numerically for a given order (O_1, ... O_L, where L > 1 but is otherwise unrelated to N, all ints). I would like to implement it in a way that the user can provide the desired method an order, something along the lines of:

Code:

Problem.Integrate(Method, Order);
where Problem specifies the equations (does not really matter here anyway). How would I implement that in a fast and flexible way?

My old class used an if-then-else loop for both the methods and the orders, which seemed fairly stupid, because the methods and orders are supposed to be known at compile time, in which case it still ran through all M_N * O_L possibilities (at worst). So, if anyone has a good suggestion, I would be very happy to hear/read it.

Thanks!

johnsfine 02-04-2009 07:39 AM

You can use an int rather than a class as a template argument.

If appropriate, you can specialize on values of that int, which seems like what you would want to do for method and maybe for order.

If you don't also do some extra work, that would change your calling syntax:
Code:

Problem.Integrate(Method, Order);
might become
Code:

Problem.Integrate<Method, Order>();
You can add layers of code (usually with big switch statements) to convert from one calling syntax to the other. That might be worth it depending on where / how the calls are used.

It may seem the big switch statement would add the same run time overhead that you're trying to remove, and if you aren't careful it might.

When instantiating an inline function, where argument values are know at the call point, the compiler will bring that knowledge into the inline function. So many constructs that look like run time "if" or "switch" will actually be handled as compile time "if" or "switch". You can use this effect:
To write the layer that changes call syntax without the run time overhead it may seem to have.
To specialize less and write code templated on Method and/or Order that seems to do run time checks, but the compiler will effectively specialize.
To specialize only linearly on each of Method and Order, not combinatorialy on both (the compiler would instantiate combinatorialy specialized versions as used).

Exactly how to do any of that in your problem space depends on details you haven't provided. It may be fairly obvious or quite tricky. (But if you provided such details, that might also stop me from providing more specific help, because your problem space is quite close to something I work on professionally, subject to non disclosure).

Often, there is a big challenge in forcing the compiler to convert run time "if" operations into compile time "if" operations early enough in the optimization process to allow other optimizations that depend on that conversion. I have often used complicated extra layers of templates to convert what is logically a compile time "if" statement into a template instantiation, that the compiler is forced to resolve early in compile time, allowing subsequent optimization of the result.

gcc has some added features (not part of the C++ standard) for coding compile time "if" statements more directly. I think that is a better approach, but I can't use that myself because I must code for multiple compilers. Note I do NOT mean "#if". A preprocessor "if" is much too early, just as a run time "if" is much too late.

When I work on such problems, I generate asm output from the compiler and look at key points in the code to see whether the compiler really did convert the important tests from run time to compile time. Of course, if you don't know x86 assembler, doing that could be a lot harder.

unihiekka 02-05-2009 06:28 AM

Thanks for the info!

I guess I'll try the TMP solution I found here (scroll down to "Switch statements"). The idea is basically the same, but I find this approach somewhat more appealing. I'll let you know if it works (or not) and if it's any good.

ta0kira 02-05-2009 01:50 PM

Something like this, maybe?
Code:

struct function {};

template <unsigned int> function integrate(function);


template <> function integrate <0> (function fFunction)
{ return fFunction; }

template <> function integrate <1> (function fFunction)
{
    function working(fFunction);
    /* integrate */;
    return working;
}

template <unsigned int Order> function integrate(function fFunction)
{ return ::integrate <1> ( ::integrate <Order - 1> (fFunction) ); }

ta0kira

PS This type of code structure requires the orders to be known at compilation time; Order must always be a constant expression. If you need to be able to vary the order for a given point in the code, you'll need to keep the order as an actual function argument and use a loop or recursion.

PPS Unless your functions can be integrated symbolically using only template semantics, you'll need to settle for at least some run-time recursion. In my opinion, unless you're dealing with very high orders, templatizing won't give you much of an advantage. Another solution, if you happen to know all combinations of order and function, is to precalculate all integrals and store them somewhere for later access. Unless you're talking about numerical integration, vs. antiderivatives.


All times are GMT -5. The time now is 09:34 AM.