views:

635

answers:

3

In MSVC 2008, I have the following code:

class Foo {
  //  Be a little smarter about deriving the vertex type, to save the user some typing.
  template<typename Vertex> inline void drawVertices(
    Elements vCount, RenPrim primitiveType, PixMaterial *mtl, Vertex const *vertices)
  {
    this->drawVertices(vCount, primitiveType, mtl, vertices, Vertex::VertexType);
  }

  virtual void drawVertices(
    Elements vCount,
    RenPrim primitiveType,
    PixMaterial *mtl,
    void const *vertices,
    uint vertexType) = 0;
};

I use it something like:

struct RenFlexibleVertexPc
{
  enum { VertexType = RenVbufVertexComponentsPc };
  float x;
  float y;
  float z;
  GraVideoRgba8 c; // Video format, not external!
};


  PixMaterial *material;
  struct Pc : RenFlexibleVertexPc
          {
            void set(Triple t, uint cl) { x = (float)t.x_; y = (float)t.y_; z = (float)t.z_; c = cl; }
          } vpc[4];
  ...
  Foo *renderer;
  renderer->drawVertices(4, RenPrimTriangleFan, material, vpc);

This works fine in MSVC 2008 SP1. However, GCC (3.4 and 4.1,2) throws a "no matching function for call to function" error, apparently not seeing the template when there is a non-template function with more arguments.

Is GCC broken, or is my code broken, and if so, why?

A: 

The definition of VertexType is already in the code (an enum). Elements is an unsigned long. Note that the code works in GCC if I explicitly cast vpc to (RenFlexibleVertexPc *)

If it's an enum why pass an object of type array 4 of struct? What is RenFlexibleVertexPc? The last argument to drawVertices should either be a constant pointer to a Vertex object or a const* to an object of a class derived from Vertex.

Foo *renderer;
renderer->drawVertices(4, RenPrimTriangleFan, material, vpc);

You are calling a function on an uninitialized pointer. I hope this is not the real code. \

dirkgently
Of course it's not uninitialized in the real code. Note the "..." part.The definition of VertexType is already in the code (an enum).Elements is an unsigned long.Note that the code works in GCC if I explicitly cast vpc to (RenFlexibleVertexPc *).
Jon Watte
The '...' is before Foo *renderer;
dirkgently
+1  A: 

There is no problem with overloading or inheritance:

#include <iostream>
#include <memory>

namespace {
    struct A {
        virtual void f()
        {
            std::cout<<"inside A's f()\n";
        }

        template <typename T> void f(T t)
        {
            std::cout<<T::i<<'\t';
            this->f();
        }
    };

    struct B : A {
        void f()
        {
            std::cout<<"hello\t";
            A::f();
        }
    };

    struct C {
        static const unsigned int i = 5;
    };

    struct D {
        enum { i = 6 };
    };
}

int main()
{
    std::auto_ptr<A> b(new B());
    b->f(C());
    b->f(D());
}

Works correctly. On the other hand, the smallest example I can find that exhibits your problem does not have inheritance or overloading:

#include <iostream>

namespace {
    struct A {
        template<class C> void print(C c)
        {
            c.print();
        }
    };
}

int main()
{
    struct B {
        void print()
        {
            std::cout << "whee!\n";
        }
    };

    A a;
    B b;
    a.print(b);
}

Note that if struct B is defined in a namespace (whether it's an unnamed namespace, or a completely different namespace, or the global namespace) instead of inside main() that this compiles without error.

I don't know enough of the standard to say if this is a bug, but it appears to be one. I've gone ahead and reported it to the GCC bug database.


And here's your answer from the GCC developers (from the link above): "Local classes cannot be template arguments."

So the code is broken. Not that it's a bad idea. In fact, C++0x removes this restriction.

I noticed the line

Note that the code works in GCC if I explicitly cast vpc to (RenFlexibleVertexPc *)

And since RenFlexibleVertexPc is not a local class this makes sense. However Pc is a local class/struct, so it is not allowed.

Max Lybbert
This was a great answer; thanks! Hope someone with reputation will vote it up.I guess I'm just trying to live in the future...
Jon Watte
+1  A: 

@OP: Specifying the template parameter is a valid approach.

renderer->drawVertices<RenFlexibleVertexPc>(4, RenPrimTriangleFan, material, vpc);

With Pete's additions, you code also compiles on Apple's GCC 4.0.1, so I suspect there's something your posted code is missing that's causing the problem.

@Max: GCC's treatment of your source is standard. Struct B is local to main(), so B (and thus main()::B::print()) is not visible outside main(). As you're probably aware, moving the definition of B outside of main() and it will compile.

outis
Thank you, I honestly did not know that. However, you will notice that the original code sample (corrected so it will compile) struct Pc (i.e., the vpc[4] variable) is defined in the function, and moving that definition outside of the function resolves the problem -- leads to compiling code.
Max Lybbert
"Original code" as in line 12 of the original post's "I use it something like: ... struct Pc : RenFlexibleVertexPc { ... } vpc[4];"
Max Lybbert
That was what was missing in the OP's post. I couldn't tell that the definition of struct Pc was local to the function (though the indentation should have been a hint).
outis