6

Ok, so I have something setup basically like this:

template<typename T> void example()
{
 std::function<int (byte*)> test = [=](byte* start) -> int
 {
  return T::magic(start);
 }
}

Ignoring how "unclean" making these naked calls is, it also doesn't compile, giving these errors:

'T' : is not a class or namespace name
'magic': identifier not found

Is there any way to be able to make a call on the generic typename T, assuming I will always be calling example() with a class that has the function magic(byte* start)? Surely I don't have to redeclare this template function for every single class that will be doing this.

I'm doing this in VC++ 2010, and it appears it may be a compiler bug. Any possible workarounds?

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
user173342
  • 1,820
  • 1
  • 19
  • 45
  • 3
    What compiler is this? AFAIK, it should work. – jpalecek Nov 13 '11 at 17:02
  • Try using "typename" for T::magic(start) – Arunmu Nov 13 '11 at 17:04
  • 1
    @ArunMu, I'm afraid that would expect `T::magic` to be a typename. And yes, the example above should work, unless screwed up elsewhere. What is a compiler? – Michael Krelin - hacker Nov 13 '11 at 17:05
  • @Michael: Yes, the example looks fine. Just in case the compiler was having trouble.. – Arunmu Nov 13 '11 at 17:09
  • @Michael Krelin A compiler is a computer program (or set of programs) that transforms source code written in a programming language (the source language) into another computer language (the target language, often having a binary form known as object code). –  Nov 13 '11 at 17:09
  • 1
    It looks like you can't template a lambda in C++11 http://stackoverflow.com/questions/3575901/can-lambda-functions-be-templated. However you could use a macro instead. – satnhak Nov 13 '11 at 17:13
  • 2
    @WTP : I am sure Michael was asking the OP, which compiler he was using :) – Arunmu Nov 13 '11 at 17:13
  • 3
    If Michael got 14k without even knowing what a compiler is, there's something seriously wrong with the stack overflow scoring system. – Benjamin Lindley Nov 13 '11 at 17:26
  • @user173342: I updated my answer with a workaround. – Benjamin Lindley Nov 13 '11 at 18:01
  • @WTP, thanks, I had a good laugh ;-) Yes, ArunMu and Benjamin, I indeed asked which compiler the OPS is using and yes, it would be fun to get this far without knowing what a compiler is ;-) – Michael Krelin - hacker Nov 13 '11 at 18:16

3 Answers3

4

The only error there is the missing semi-colon. Once that is fixed, it works fine.

#include <iostream>
#include <functional>

typedef unsigned char byte;

template<typename T> void example()
{
    std::function<int (byte*)> test = [=](byte* start) -> int
    {
        return T::magic(start);
    }; // <--------------------------------- You were missing that
}

struct Foo {
    static int magic(byte*);
};

int Foo::magic(byte* start)
{
    std::cout << "magic\n";
}

int main()
{
    example<Foo>();
}

http://ideone.com/dRdpI

As this appears to be a bug in VC10's lambda implementation, a possible workaround is to create a local functor class:

template<typename T> void example()
{
    struct Foo {
        int operator()(byte * start) { return T::magic(start); }
    };

    std::function<int (byte*)> test = Foo();    
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • well the semicolon and perhaps the non-standard `byte` typename – sehe Nov 13 '11 at 17:17
  • Ya, sorry, I wrote up the example pretty quick without checking. My actual code has the semicolon and the byte typedef in it. I'm not sure what is going wrong here. I'm using VC++ 2010 to compile, what are you using? – user173342 Nov 13 '11 at 17:31
  • @user173342: I was using GCC 4.5. But I just tested it in VC10, and I now see your errors. Looks like a compiler bug. – Benjamin Lindley Nov 13 '11 at 17:36
  • Ok, thanks, and I found a solution elsewhere thanks to your sanity check that my code was valid. Basically, setting an auto variable to the static function before the lambda allows the lambda to capture that auto variable and make the call through it. – user173342 Nov 13 '11 at 18:03
  • @user173342: please can you post _**that**_ as the answer? I've wasted time trying to find nearly that solution, and the accepted answer isn't really that helpful as your woraround... – sehe Nov 13 '11 at 18:09
  • @sehe: Why do you say my workaround isn't helpful? Is it a functional problem, or just an aesthetic one? – Benjamin Lindley Nov 13 '11 at 18:14
  • @BenjaminLindley: You know full well I didn't say that :) Also _if there were_ a problem with 'your workaround', **then** it would be a principled one, I guess. I'd like to know how to get the _original_ code working (as close as possible to), not how to rewrite the whole thing in non-lambda style :) So, you answer is +1, but the other workaround would be what I think people are looking for when researching this problem. When the compiler is giving us trouble, we already know that we can 'lay off the lambda dope' - but we don't want to :) – sehe Nov 13 '11 at 18:20
  • @user173342: sehe is right, you should post that as an answer yourself. You found the answer, you should get credit for it. You'll get a +1 from me. – Benjamin Lindley Nov 13 '11 at 18:29
3

I reproduced the problem with VS2010. You need to call the example function, though:

#include <functional>

struct SomeT { static int magic(unsigned char*) { return 42; } };

template<typename T> void example()
{
    std::function<int (unsigned char*)> test = [=](unsigned char* start) -> int
    {
        return T::magic(start);
    };
}

int main()
{
    example<SomeT>();
}

Update based on a comment by the OP:

This works:

#include "stdafx.h"
#include <functional>

struct SomeT { static int magic(unsigned char*) { return 42; } };

template<typename T> void example()
{
    auto func = T::magic;
    std::function<int (unsigned char*)> test = [=](unsigned char* start) -> int
    {
        return func(start);
    };
}

int main()
{
    example<SomeT>();
}

I have been looking for workarounds, but none working yet, I tried up to and including this nice permutation, but no luck yet:

template<typename T> void example()
{
    static const T* workaround;
    std::function<int (unsigned char*)> test = [=](unsigned char* start) -> int
    {
        typedef decltype(*workaround) innerT;
        return innerT::magic(start);
    };
}

Tough one this...

sehe
  • 374,641
  • 47
  • 450
  • 633
  • No need to make an object of type `T`. I think the OP meant he did `auto x = T::magic;`, then inside the lambda, he called `x(start);` – Benjamin Lindley Nov 13 '11 at 18:19
  • Ya, it's a static function. You'd want to set the auto variable to the actual function itself, not the class. The class is never instantiated. – user173342 Nov 13 '11 at 18:22
  • @BenjaminLindley: good thinking, I wasn't thinking of function types. Tested and fixing answer – sehe Nov 13 '11 at 18:22
  • You see, @user173342: this is exactly why it was helpful to post that workaround, because people could be running in circles on how to approach that with a non-publicly-constructable type. It took _me_ two tries :) – sehe Nov 13 '11 at 18:24
0

I'm sure you're doing something wrong, because I'm able to call a static method using the syntax T::f() inside the lambda:

#include <iostream>
#include <functional>
struct A
{
   static void f() { std::cout << "called" << std::endl; }
};

template<typename T> 
void example()
{
  std::function<void()> test = [=]() { T::f(); };
  test();
}
int main() {
        example<A>();
        return 0;

}

Demo : http://ideone.com/IPakS

So please post more info. Till this is the answer. Try compare your code with this and see if you're doing something terribly wrong.

If you're using GCC, did you compile your code with -std=c++11 option?

Nawaz
  • 353,942
  • 115
  • 666
  • 851