4

For some strange reason, I can't get the template arguments in this one piece of code to implicitly cast to a compatible type.

#include <type_traits>

template <typename T, unsigned D>
struct vec;

template <>
struct vec<float, 2> {
    typedef float scalar;
    static constexpr unsigned dimension = 2;

    float x, y;
    float&       operator[] (unsigned i)       { return (&x)[i]; }
    float const& operator[] (unsigned i) const { return (&x)[i]; }
};


template <typename L, typename R>
struct add;

template <typename L, typename R, unsigned D>
struct add<vec<L, D>, vec<R, D>> {
    typedef vec<L, D> left_type;
    typedef vec<R, D> right_type;
    typedef vec<typename std::common_type<L, R>::type, D> return_type;

    add(left_type l, right_type r)
        : left(l),
          right(r)
    {}

    operator return_type() const
    {
        return_type result;
        for (unsigned i = 0; i < D; ++i)
            result[i] = left[i] + right[i];
        return result;
    }

    left_type  left;
    right_type right;
};


template <typename L, typename R, unsigned D>
add<vec<L, D>, vec<R, D>>
operator+(vec<L, D> const& lhs, vec<R, D> const& rhs)
{
    return {lhs, rhs};
}


int main()
{
    vec<float, 2> a, b, c;
    vec<float, 2> result = a + b + c;
}

Fails with:

prog.cpp: In function 'int main()':
prog.cpp:55:36: error: no match for 'operator+' in 'operator+ [with L = float, R = float, unsigned int D = 2u](((const vec<float, 2u>&)((const vec<float, 2u>*)(& a))), ((const vec<float, 2u>&)((const vec<float, 2u>*)(& b)))) + c'

So if I'm correct, the compiler should see the code in the main function as this:

  • ((a + b) + c)
  • compute a + b
  • cast the result of a + b from add<...> to vec<float, 2> using the conversion operator in add<...>
  • compute (a + b) + c

But it never does the implicit cast. If I explicitly cast the result of (a + b) to a vec, the code works fine.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
vedosity
  • 720
  • 4
  • 15
  • That is an awful lot of code to wade through. Are you able to simplify your example? – Oliver Charlesworth Jun 21 '11 at 22:21
  • 5
    @Oli: Actually this is a pretty much boiled-down and straight-forward expression template implementation. This contains almost no algorithmic code, it's mostly the mechanics to make this work. I really don't see what's to criticize here. – sbi Jun 21 '11 at 22:31
  • @sbi: The problem is that I'd like to help, but I'm tired and really don't want to read all that code! Oh well... – Oliver Charlesworth Jun 21 '11 at 22:32
  • @sbi I've actually been trying to figure out exactly what this is called. I knew I had seen it before, but I couldn't remember the name. Thanks! – vedosity Jun 21 '11 at 22:44
  • You are programming what is called "expression templates". Try searching for that term. For example [here](http://www.angelikalanger.com/Articles/Cuj/ExpressionTemplates/ExpressionTemplates.htm#Dot%20Product) is a decent article by Angelika Langer. This is considered an advanced technique. So have fun learning! :-) – Howard Hinnant Jun 21 '11 at 23:18
  • @Oli: I can relate to your feeling very well, but this time it's not the fault of the question. `:)` – sbi Jun 22 '11 at 07:19
  • @franticfantom: I think the technique was invented by Todd Veldhuizen when he created blitz++. He wrote a quite [famous article](http://www10.informatik.uni-erlangen.de/~pflaum/pflaum/ProSeminar/exprtmpl.html) >15 years ago, but I have no doubt that any article by Klaus Kreft & Angelika Langer is pretty good (especially with Howard Hinnant recommending it `:)`). However, you might find the [Wikipedia entry for expression templates](http://en.wikipedia.org/wiki/Expression_templates) interesting, too, since it uses vector expressions as an example. – sbi Jun 22 '11 at 07:30

2 Answers2

5

I'm going to side-step your actual problem and instead make a recommendation: Rather than writing all of this complicated boilerplate from scratch, have a look at Boost.Proto, which has taken care of all the tricky details for you:

Proto is a framework for building Domain Specific Embedded Languages in C++. It provides tools for constructing, type-checking, transforming and executing expression templates. More specifically, Proto provides:

  • An expression tree data structure.
  • A mechanism for giving expressions additional behaviors and members.
  • Operator overloads for building the tree from an expression.
  • Utilities for defining the grammar to which an expression must conform.
  • An extensible mechanism for immediately executing an expression template.
  • An extensible set of tree transformations to apply to expression trees.

See also the library author's Expressive C++ series of articles, which more-or-less serve as an (excellent) in-depth Boost.Proto tutorial.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • I'll take a look at it, but it looks quite intimidating. All I want to do is provide specializations of certain operations so that I can optimize them. Like, for example, (a * b) + (c * d) can be optimized with SIMD. So, I would override add, mul> to call vDSP_vmma (I'm using vDSP for SIMD optimizations). – vedosity Jun 22 '11 at 00:54
  • @franticfantom : Intimidating compared to writing expression templates from scratch? ;-] If you follow the tutorial, I think you'll find the number of lines of code for a Proto-based implementation to be an order of magnitude less than a hand-written expression template, plus you'll run into far fewer idiosyncrasies like the one that prompted this question to begin with. :-] – ildjarn Jun 22 '11 at 01:15
  • 1
    @franticfantom : Also, you should bug [Joel Falcou](http://stackoverflow.com/users/737268/) about the current state of [NT2](http://nt2.sourceforge.net/), which I understand is being rewritten around Boost.Proto and may consequently **perfectly** solve your problem. – ildjarn Jun 22 '11 at 01:16
  • @ildjarn: I, too, often preferred template error messages from my own mess over those which some boost code would vomit at me. – sbi Jun 22 '11 at 07:49
  • @sbi : Given that Eric Niebler is [leading the crusade to improve library error messages](http://cpp-next.com/archive/2010/09/expressive-c-why-template-errors-suck-and-what-you-can-do-about-it/), I'll warrant that Boost.Proto yields easier to understand errors than anyone else's expression template code. "*Bad template errors are **library bugs** and should be reported as such.*" Indeed, ease of debugging (via well-commented, well-placed static assertions) is touted as one of the primary reasons to use Boost.Proto in the first place. No offense regarding your own code implied. ;-] – ildjarn Jun 22 '11 at 13:19
  • So I've spent the last couple days learning Boost.Proto, and I think this is actually a better solution in the long run. Although the error's I've gotten are a bit harder to read, Boost.Proto is a lot more flexable, and I'm thinking about even doing optimizations with OpenCL when working with large arrays of points and the formula is big enough. And that is as simple as switching contexts used to evaluate the expression :). – vedosity Jun 23 '11 at 21:07
  • @franticfantom : I'm glad you like Proto. :-] Again, I recommend pinging Joel Falcou about the status of the NT2 Proto rewrite, since if it's currently in a usable state, he may have the exact Proto grammars and backends (and optimizations) you want already written. He's almost always in `#boost` on Freenode IRC. – ildjarn Jun 23 '11 at 21:11
  • nt2 simd back end is indeed usable and is currently being ported as a stand alone boost library during google summer of code. Any other infos on #boost or #nt2 @ freenode – Joel Falcou Jun 25 '11 at 21:05
2

Most conversions are not used during template argument deduction.

You rely on template argument deduction when you call your operator+ overload: it is only callable where both arguments are of type vec<...>, but when you try to call it the left-hand argument is of type add<...>. The compiler is not able to figure out that you really mean for that overload to be called (and it isn't allowed to guess), hence the error.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • aka, write the `operator+` twice. :/ – Xeo Jun 21 '11 at 22:41
  • Four times: (1) `vec + vec`, (2) `vec + add`, (3) `add + vec`, (4) `add + add`. All four combinations are possible: `v + v`, `(v + v) + v`, `v + (v + v)`, and `(v + v) + (v + v)`. Alternatively (and preferably), if you have your classes in a namespace, you should be able to relax the restrictions on the template (i.e., have your `operator+` take any arbitrary `T` and `U` instead of `vec<...>` and a `vec<...>`) and let ADL do the right thing. – James McNellis Jun 21 '11 at 22:43
  • @James The first method becomes impossible when I add subtract, multiply, divide, etc. The latter I've had a little bit of trouble with, but I still have some approaches left to try. If I make operator+ take T and U, that means I need to specialize add to simplify expressions on the left and right side, correct? – vedosity Jun 21 '11 at 22:54
  • I'll have to think about this a bit. For a quick answer, someone else would know better than me (anyone else, probably :(). I've not really used expression templates. I'll think about it though. (You might want to unaccept this if you want to get a better answer to the "what should I do to make this work?" question; a lot of people avoid answering questions with an accepted answer.) – James McNellis Jun 21 '11 at 22:59
  • @James: "you might want to unaccept this", that's impressive. :-) – Cheers and hth. - Alf Jun 21 '11 at 23:26
  • The wikipedia article about the [Curiously Recursive Template Pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) has an example that shows how to get out of this problem without writing `operator+` many times. – Lambdageek Jun 21 '11 at 23:28
  • Sweet! I finally got it to work nicely. It took some tinkering, but it works now. Letting operator+ take any arbitrary option T, U was a step in the right direction, plus a whole bunch of other stuff (I made an eval(..) function that basically returns the result of add<...> and other functors, and specialized it for scalars and vectors). The code really isn't as bad as I thought it would be. – vedosity Jun 22 '11 at 03:03
  • 1
    @Alf: Well, I'm not an expert on the subject and the important thing is that the OP gets the best possible answer. I have enough "points" as it is :-). – James McNellis Jun 22 '11 at 03:03
  • @franticfantom: Good to hear! Would you mind posting an answer with what your solution ended up being or an explanation of what you ended up doing, both for our edification and for those who may stumble across this question in the future? – James McNellis Jun 22 '11 at 03:05
  • @James Maybe tomorrow. I haven't exactly cleaned up the code at all, so it still has remnants of previous attempts to get it working. Plus it is a lot of code to post, I want to simplify it a little. – vedosity Jun 22 '11 at 03:41
  • @James: You still have to break through the 100k barrier. (And that's coming from me, who fails to make the 50k one for months.) – sbi Jun 22 '11 at 07:20