1

I was trying to use a local class as a way to simulate local functions. The local class defines several static helper functions, where each static functions needs to access a static array defined in the function scope. The thing is working for a normal function, but I have a linking error when using a template function. The code is:

#include <iostream>

double test_local (double x)
{
    static const double coeff[3]={ 0, 1, 2 };

    struct local_functions
    {
        static double f0 (double x)
        {
            static const double c0=coeff[0]+coeff[1];
            return c0+x;
        }

        static double f1 (double x)
        {
            static const double c1=coeff[1]+coeff[2];
            return c1+x;
        }

        static double f2 (double x)
        {
            static const double c2=coeff[2]+coeff[0];
            return c2+x;
        }      
    };

    return local_functions::f0(x)+local_functions::f1(x)+local_functions::f2(x);
}

template<class t>
t test_local_tmpl (t x)
{
    static const t coeff[3]={ 0, 1, 2 };

    struct local_functions
    {
        static t f0 (double x)
        {
            static const t c0=coeff[0]+coeff[1];
            return c0+x;
        }

        static t f1 (t x)
        {
            static const t c1=coeff[1]+coeff[2];
            return c1+x;
        }

        static t f2 (t x)
        {
            static const t c2=coeff[2]+coeff[0];
            return c2+x;
        }
    };

    return local_functions::f0(x)+local_functions::f1(x)+local_functions::f2(x);
}

int main (int argc, char** argv)
{
    double result=test_local (1e0);

    // uncommenting next line generates a linking error
    // double result_tmpl=test_local_tmpl (1e0);

    std::cout << result << std::endl;
    return 0;
}

the non template function works fine (it prints 9), while if I try to invoke the template version, it compiles fine but does not link under g++-4.6:

g++ -c -g local_class.cpp && g++ local_class.o -o local_class
Undefined symbols:
  "coeff", referenced from:
      double test_local_tmpl<double>(double)::local_functions::f2(double) in local_class.o
      double test_local_tmpl<double>(double)::local_functions::f2(double) in local_class.o
      double test_local_tmpl<double>(double)::local_functions::f1(double) in local_class.o
      double test_local_tmpl<double>(double)::local_functions::f1(double) in local_class.o
      double test_local_tmpl<double>(double)::local_functions::f0(double) in local_class.o
      double test_local_tmpl<double>(double)::local_functions::f0(double) in local_class.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Is this the expected beahaviour, i'm missing something or what?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Giuliano
  • 640
  • 3
  • 15
  • possible duplicate of [Using local classes with STL algorithms](http://stackoverflow.com/questions/742607/using-local-classes-with-stl-algorithms). The restriction of not using local classes in templates has been removed from C++11, don't know if gcc4.6 implements that though -- on a second thought, I am not even sure it is a duplicate I find it hard to process the question with the code laid out like that. – David Rodríguez - dribeas Mar 23 '12 at 15:56
  • @DavidRodríguez-dribeas: I have problems when linking, **the code compiles fine**. In any case adding the -std=c++0x flag gives the same problem. – Giuliano Mar 23 '12 at 16:08
  • @ildjarn: The code **works perfectly** with the non-template version of the function. It compiles, links and prints '9', just as expected. Only the template version gives the problem. – Giuliano Mar 23 '12 at 16:15
  • @DavidRodríguez-dribeas: I think, it is not a duplicate. The question is something else. – Nawaz Mar 23 '12 at 16:17
  • @David this code is not a duplicate of that question.. – Johannes Schaub - litb Mar 23 '12 at 16:20
  • @Nawaz, litb: That is why I added the comment *on second thought*... – David Rodríguez - dribeas Mar 23 '12 at 16:33
  • Have you tried invoking the template properly? With a after the name? Also f0 is defined with (double x) not (t x) like it should be. – std''OrgnlDave Mar 23 '12 at 17:30
  • @OrgnlDave : What do you mean 'properly'? Explicitly defining the template type is unnecessary here, as its deducible by the compiler. – ildjarn Mar 23 '12 at 17:47
  • @ildjarn Indeed one would think so, but it helps to eliminate possible issues, doesn't it? Especially with static const variables. Anyhow in the answer to the next question it was said that it was tried and didn't work. Looks like a scoping bug in GCC, as a side-note, compiles and works fine in MSVC. I wonder if it would compile fine if coeff was made just const, instead of static const. – std''OrgnlDave Mar 23 '12 at 18:21
  • @OrgnlDave Nope, local classes may not access automatic variables in the scope of the enclosing function. – Giuliano Mar 23 '12 at 18:27
  • @OrgnlDave : "*it helps to eliminate possible issues, doesn't it?*" Only if the compiler deduced incorrectly, but if the compiler deduced `t` as `double` then explicitly defining the template argument is merely redundant. I don't mean to imply that doing so could _hurt_ anything, but I'd be amazed if it helps. :-] – ildjarn Mar 23 '12 at 20:54

2 Answers2

1

This is a GCC bug. There is no error in your code. Please file a bug report.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

Workaround to circumvent the problem while waiting for a genuine solution from the gcc developers. I must admit that my final decision was to switch to a traditional implementation without helper functions. It is much less elegant (of course) but works and it is all that matters.

Anyway I present the "solution" here since it could be useful in other contexts (maybe). Hope the code formatting will not hurt your eyes :) this time.

#include <iostream>
#include <complex>

template<class t>
t test_local_tmpl (t x)
{
  struct local_functions
  {
     static const t* coeff ()
     {
        static const t c[]={0,1,2};
        return c;
     }

     static t f0 (t x)
     {
        static const t c0=coeff()[0]+coeff()[1];
        return c0+x;
     }

     static t f1 (t x)
     {
        static const t c1=coeff()[1]+coeff()[2];
        return c1+x;
     }

     static t f2 (t x)
     {
        static const t c2=coeff()[2]+coeff()[0];
        return c2+x;
     }
  };

  return local_functions::f0(x)+local_functions::f1(x)+local_functions::f2(x);
}

int main (int argc, char** argv)
{
  std::cout << test_local_tmpl (1e0) << std::endl;
  std::cout << test_local_tmpl (std::complex<double>(1e0)) << std::endl;
  return 0;
}

And the output is

$ g++-mp-4.6 -c -g local_class.cpp && g++-mp-4.6 local_class.o -o local_class
$ ./local_class 
9
(9,0)

Anyway it seems that local classes are such a corner feature in the language, that probably they never did a trip out from Alexandrescu's book on C++ designs. The fact that the bug was not discovered before confirms this IMHO.

Giuliano
  • 640
  • 3
  • 15