views:

179

answers:

5

Here is an (artificial) example of using a function that returns an anonymous struct and does "something" useful:

#include <iostream>

template<typename T>
T* func( T* t, float a, float b )
{
    if(!t) 
    {
        t = new T;
        t->a = a;
        t->b = b;
    }
    else
    {
        t->a += a;
        t->b += b;
    }
    return t;
}

struct  
{
    float a, b;
}* foo(float a, float b)
{
    if(a==0) return 0;
    return func(foo(a-1,b), a, b);
}

int main()
{
    std::cout << foo(5,6)->a << std::endl;
    std::cout << foo(5,6)->b << std::endl;

    void* v = (void*)(foo(5,6));
    float* f = (float*)(v); //[1] delete f now because I know struct is floats only.

    std::cout << f[0] << std::endl;
    std::cout << f[1] << std::endl;

    delete[] f;

    return 0;
}

There are a few points I would like to discuss:

  1. As is apparent, this code leaks, is there anyway I can NOT leak without knowing what the underlying struct definition is? see Comment [1].
  2. I have to return a pointer to an anonymous struct so I can create an instance of the object within the templatized function func, can I do something similar without returning a pointer?
  3. I guess the most important, is there ANY (real-world) use for this at all? As the example given above leaks and is admittedly contrived.

By the way, what the function foo(a,b) does is, to return a struct containing two numbers, the sum of all numbers from 1 to a and the product of a and b.

EDIT: Maybe the line new T could use a boost::shared_ptr somehow to avoid leaks, but I haven't tried that. Would that work?

EDIT2: I think I was just trying to delete the anonymous struct as an array of floats, something like float* f = new float[2]. Which might be wrong, as the comment below suggests, so what can be done? can I delete at all?

EDIT3: I can compile and run this code "as-is" on VS2008, maybe some non-standard extensions might be being used by VS, but it does run and gives 15 and 30 as the answer.

EDIT4: From the answers I believe this contraption is a VS2008 specific entity, it is not standards compliant and thus not portable. Too bad though, I would have liked to see what voodoo the Stackoverflow or Boost people came up with if this was in their arsenal :). Thanks all.

+9  A: 

For now, your code is not portable; it will, for example, not build with gcc.

Section 14.3.1/2 of the standard says:

A local type, a type with no linkage, an unnamed type or a type
compounded from any of these types shall not be used as a template-
argument
for a template type-parameter.

See item 488 in the C++ Standard Core Language Defect Reports, Revision 69 and Paper N2657 for one possible evolution.

UPDATE 1

Assuming that your code were well-formed, then:

  1. you may be able to rewrite:

    std::cout << foo(5,6)->a << std::endl;
    

    as

    std::cout << std::auto_ptr(foo(5,6))->a << std::endl;
    
  2. you may return an anonymous struct by-value provided that the anonymous struct had a constructor taking another type (anonymous or not, that you'd be able to initialize inside the body of your method) -- except, of course, how do you specify a constructor for an anonymous struct? :)

  3. no real-world use that I can see other than an extremely convoluted way of trying not to assign an explicit name to the structure; one would typically use anonymous structs (not technically legal in C++, but supported by various compilers as extensions) in order to not pollute the namespace, typically by instantiating one right away (you may for example see one-shot functors being instantiated and passed down as anonymous structs -- again, technically not legal C++.)

UPDATE 2

Thank you gf for the link to the relevant portion of the C++ standard concerning new types which may not be defined in a return type.

UPDATE 3

Bringing this one out here from the comments: calling delete[] on memory allocated with new (as opposed to new[]) is an invitation to heap corruption. Calling delete on a pointer whose type you do not know is technically undefined (which destructor should get called?) but in the case of PODs (your anonymous struct being one) you can get away with it in this horrible hackish way:

 delete (int*)f;

Of course, were your code magically well-formed, std::auto_ptr would have been able to retain the anonymous type and would have taken care of calling delete for you correctly and gracefully.

vladr
So this is just VS2008 trickery, interesting. Thanks.
Akanksh
Thanks for the possible evolution in this context.
Akanksh
@Vlad: Even with N2657 integrated its non-portable due to type definitions not being allowed in return types.
Georg Fritzsche
@gf, true, when I say `assuming your code were well-formed` it's a big assumption going beyond N2657, but let's pretend for the sake of argument that `g++` supported that too as an extension in the future (`gcc` will swallow it just fine when building C, just as MSVC did for the OP...) Thanks for the standard reference BTW! :)
vladr
@Vlad: Wrote that before the update. Even then, there are no anonymous structs in C++ as Kirill says. I don't think a discussion of what-would-be-if-the-language-were-different gives us anything :)
Georg Fritzsche
+3  A: 

C++ Standard does not allow anonymous structures.

Kirill V. Lyadvinsky
You might want to make that more precise. 14.3.1/2 outlaws them for a specific situation, and it's obvious that rule is only needed if they are generally allowed.
MSalters
@MSalters, *Unnamed structures* are generally allowed, but not *anonymous*. The first is this one, which C++ supports: `struct { int i; } a; a.i = 0;` (the type has no name). The second is this one, which C++ does not support: `struct { int i; }; i = 0;` (the type has no name, and it escapes into the surrounding scope).
Kirill V. Lyadvinsky
+1  A: 

I can't think of any reasonable usage. Apart the memory leaks, this is a very unreadable way of achieving the goal you want. It makes your reader think a lot what the code is doing. And also it is not known who should delete 'f' in the main(). And should it be deleted with delete[] or delete?

I would use a class taking the 'a' and 'b' in the constructor. It will have two methods for getting the two calculated struct members. And inside the class there will be private mehotds, using plain loops to calculate the things you want. Then your API will look like this:

void main()
{
   MyCalculator myCalc(5, 6);
   double sumOfAllNumbers = myCalc.getSumOfAllNumbers();
   double product = myCalc.getProduct();
}
m_pGladiator
+1 for "very unreadable way of achieving the goal you want".
Michael Aaron Safyan
Yes, I think I have mentioned in my question that this is a contrived example and I could do that thing it does in a MUCH simpler way as you have suggested. However, my question was simply to find a way where this might be THE way to do things. I believe it would be handy if a function returned something that I could access various parts of and not have to make a special struct. For example the usage : foo(5,6)->a is elegant and I do not have to define a seperate struct name for it. Anyhow, the code is not portable and it is VS2008 specific. Thanks.
Akanksh
Sorry, but 'foo(5,6)->a' might be handy, but I don't think it is elegant. It is hard to read because normally you have calls like "object->method()". In this case it is exactly the opposite - the left side is a function returning a struct and the right side is just a member. Also it is hard to debug. But this example is good for student tests ;)
m_pGladiator
+4  A: 

What you are doing is not possible in standard C++ - type definitions are not allowed in return types as per §8.3.5/6 (function declarators, C++03):

Types shall not be defined in return or parameter types.

Visual Studio is non-compliant in this case.

Georg Fritzsche
+1 for the reference, was actually looking for that :)
vladr
+1  A: 

A close approximation of an anonymous struct is a tuple. Boost::Tuple is available anywhere now, and there's another in TR1 [which I assume is distributed with VS2008] with a near identical interface.

#include <boost/tuple/tuple.hpp>

template<typename T>
boost::tuple<T, T>* func(boost::tuple<T, T>* t, float a, float b ) {
    if(!t) {
      t = new boost::tuple<T, T>(a, b);
    } else {
      boost::get<0>(*t) += a;
      boost::get<1>(*t) += b;
    }
    return t;
}

boost::tuple<float, float>* foo(float a, float b) {
    if(a==0) return 0;
    return func(foo(a-1,b), a, b);
}

As others have said, the overall scheme is pretty flimsy, but I wanted to focus on the tuple rather than the design.

Dennis Zickefoose
Good discussion, but can you better qualify what you meant by "close approximation"? (i.e. programmer POV/intent as opposed to compiler POV, where the programmer intent may be not to have to come up with yet another type name for the Nth incarnation of a tuple, may code readability suffer or flourish as a result.)
vladr