Jump to content


Nice C++ riddle/problem


29 replies to this topic

#1 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 May 2007 - 11:42 AM

While browsing through the archive of a particular website of which I'm not yet mentioning it's name (because the answer can be found there ;)), I stumbled on a nice C++ problem:

Is it possible to implement a function f() that returns a pointer to itself, such that the following program compiles and works as expected. And if so, how?

typedef FuncPtr /* the signature of f() */;

int main()
{
    FuncPtr fp1 = f();    // calls f()
    FuncPtr fp2 = fp1();  // also calls f()
    FuncPtr fp3 = fp2();  // guess what ;)
}

Of course, it has to be standard compliant and fully portable ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#2 SigKILL

    Valued Member

  • Members
  • PipPipPip
  • 200 posts

Posted 30 May 2007 - 01:34 PM

Possibly cheesy, but in vs.net the following works

class dummy;

typedef dummy&(*FuncPtr)(void);


class dummy {

private:

	FuncPtr	m_ptr;

public:

	dummy( FuncPtr ptr ) {

		m_ptr = ptr;

	};


	operator FuncPtr() {

		return m_ptr;

	};

};


dummy	&f() {

	static dummy ret(f);

	std::cout << "Called f().\n";

	return ret;

};

If this is a valid solution you better come up with something harder the next time ;)

#3 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 May 2007 - 02:10 PM

Yes, that was the answer I was looking for, although your solution isn't very thread-safe (then again, standard C++ says nothing about threads anyway) ;). You'd be surprised how many people would say it isn't possible as they're trying to define a recursive type of a pointer to [a function that returns a pointer to] ad infinitum ;)

Why the refence and the local static?
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#4 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 May 2007 - 02:19 PM

This one is also nice:

What does the following program output, and why:
#include <iostream>

int main()
{
    int x = 0;
    for (int i = 0; i < 100; i++);
        // how many times will the following line be executed ?????/
        ++x;

    std::cout << x << std::endl;
}

C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#5 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 30 May 2007 - 02:27 PM

.oisyn said:

You'd be surprised how many people would say it isn't possible as they're trying to define a recursive type of a pointer to [a function that returns a pointer to] ad infinitum ;)

That's because its a trick question, the trick being this:

.oisyn said:

returns a pointer to itself

The solution returns a functor.

Personally I'd just use boost::function

#6 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 30 May 2007 - 02:30 PM

.oisyn said:

// how many times will the following line be executed ?????/
Is that supposed to be a continuation '\' at the end or is it a double bluff? (and i did notice the semicolon at the end of the for line)

#7 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 May 2007 - 02:50 PM

dave_ said:

That's because its a trick question, the trick being this:

The solution returns a functor.
I meant the term 'pointer' in the broadest sense of the word. I consider a boost::shared_ptr a pointer as well.

Quote

Personally I'd just use boost::function
Which isn't going to help you here without using the same temporary object.

boost::function<boost::function<boost::function<... etc ...>()>()>()> f();

dave_ said:

Is that supposed to be a continuation '\' at the end or is it a double bluff? (and i did notice the semicolon at the end of the for line)
I made no mistakes in the code ;). You're on the right track, but you're probably missing something (the output is indeed '0').
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#8 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 30 May 2007 - 04:23 PM

Its some other sort of continuation, at least thats what my compiler tells me:
warning C4010: single-line comment contains line-continuation character

whats special about '??/' ?



.oisyn said:

boost::function<boost::function<boost::function<... etc ...>()>()>()> f();
oh yeh of course

#9 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 30 May 2007 - 04:35 PM

dave_ said:

Its some other sort of continuation, at least thats what my compiler tells me:

Well, yes, that's the point. / is no continuation character, but \ is. However, ??/ is the trigraph sequence for \
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#10 poita

    Senior Member

  • Members
  • PipPipPipPip
  • 322 posts

Posted 30 May 2007 - 04:38 PM

'??/' is a trigraph that becomes '\' which makes the '++x;' part of the comment.

In any case, the for loop is empty because it is ended with a ';'

[edit]

Too late! :P

#11 Reedbeta

    DevMaster Staff

  • Administrators
  • 5310 posts
  • LocationSanta Clara, CA

Posted 31 May 2007 - 06:28 AM

Astounding! I never realized these trigraph things existed before.
reedbeta.com - developer blog, OpenGL demos, and other projects

#12 SigKILL

    Valued Member

  • Members
  • PipPipPip
  • 200 posts

Posted 31 May 2007 - 12:20 PM

.oisyn said:

Why the refence and the local static?

Stupidity.

I thought it would be nice to look at the assembler output so we could see that the function actually returns a pointer to itself (all that dummy class stuff is just forcing C++ to do what you want). This is the optimized output of vs.net 2003 (btw. f() now returns dummy by value):


int main(int argc, char*argv[]) {

	FuncPtr fp1 = f();    // calls f()

00401020  mov         eax,dword ptr [__imp_std::cout (403024h)] 

00401025  sub         esp,8 

00401028  push        offset string "Called f().\n" (40314Ch) 

0040102D  push        eax  

0040102E  call        dword ptr [__imp_std::operator<< (403020h)] 

    FuncPtr fp2 = fp1();  // also calls f()

00401034  lea         ecx,[esp+8] 

00401038  push        ecx  

00401039  call        f (401000h) 

    FuncPtr fp3 = fp2();  // guess what ;)

0040103E  lea         edx,[esp+10h] 

00401042  push        edx  

00401043  call        dword ptr [eax] 

00401045  add         esp,10h 

	__asm int 3;

00401048  int         3    

	return 0;

00401049  xor         eax,eax 

};


If you know your assembler you'll see that f() returns a pointer to itself. However, the first call to f() is inlined, the second time it calls f() directly instead of through a pointer and the third time it calls f() through a pointer. I know why this happends, but it's also kind of funny.

If anyone asks why I'm having an "__asm int 3" in there: It's an habit from the old days when using a debugger that wouldn't break in release builds...

#13 roel

    Senior Member

  • Members
  • PipPipPipPip
  • 698 posts

Posted 31 May 2007 - 07:39 PM

Reedbeta said:

Astounding! I never realized these trigraph things existed before.

I second that. I love this thread :)

#14 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 01 June 2007 - 09:35 AM

SigKILL said:

Stupidity.

I thought it would be nice to look at the assembler output so we could see that the function actually returns a pointer to itself (all that dummy class stuff is just forcing C++ to do what you want). This is the optimized output of vs.net 2003 (btw. f() now returns dummy by value):

[..snip..]
If you know your assembler you'll see that f() returns a pointer to itself. However, the first call to f() is inlined, the second time it calls f() directly instead of through a pointer and the third time it calls f() through a pointer. I know why this happends, but it's also kind of funny.

Can you post the assembly code for f()? I wonder why it pushes a parameter on the stack before calling f() or the function pointer. The parameter that is passed seems to be the address of a local variable. Maybe f() uses it to store the function return value in, which is the dummy object, but eax is also used so I don't understand why the compiler does that.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#15 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 01 June 2007 - 10:26 AM

Another one: What should this program output? (not all compilers get this correct)

[code=c++]#include <iostream>

struct A
{
operator int() { return 0; }
};

void foo(int)
{
std::cout << "foo(int)" << std::endl;
}

template<class T> void bar(T t)
{
foo(t);
}

void foo(char)
{
std::cout << "foo(char)" << std::endl;
}

void foo(A)
{
std::cout << "foo(A)" << std::endl;
}

int main()
{
bar('3');
bar(3);
bar(A());
}[/code]
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#16 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 01 June 2007 - 02:22 PM

A guess, seems obvious, is it: char, int A?


I dont know is the last one supposed to be a trick? A()() would give an int...

#17 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 01 June 2007 - 03:21 PM

A()() would give an error...

My guess would be int, int, int, since foo(char) and foo(A) is declared after bar().
"Stupid bug! You go squish now!!" - Homer Simpson

#18 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 01 June 2007 - 09:20 PM

The answer is:

foo(int)
foo(int)
foo(A)

For non-dependent names in templates, name lookup is done at point of definition. For dependent-names, however, ADL (Argument Dependent Lookup) is also performed at point of instantiation, next to the normal lookup. Since foo is called with a template argument, it is a dependent name.

For bar<char>, foo is lookup at definition time and ::foo(int) is found, and the namespace associated with char is also searched at point of instantiation (in main()). However, char has no associated namespace and so foo(int) is called. Likewise for bar<int>. For bar<A>, the namespace associated with A is again searched at point of instantiation, which is the global namespace, which contains foo(A).

A::operator int() is not used in this example and was purely meant to put you on the wrong foot :P

VC++ has this incorrect and only looks up at point of instantiation time (which is documented in the MSDN as non-standard behaviour)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#19 SmokingRope

    Valued Member

  • Members
  • PipPipPip
  • 210 posts

Posted 04 June 2007 - 10:40 AM

Although char doesn't have an associated namespace, is there some way to associate it with one, i.e.
typedef char myChar;
void foo(myChar){};
// or 
namespace MyNamespace
{
  typedef char myNamespaceChar;
  void foo(myNamespaceChar){};
}

void main()
{
  bar(myChar('3'));
  bar(myNamespace::myNamespaceChar('3'));
}


#20 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 04 June 2007 - 01:24 PM

No, myChar and myNamespace::myNamespaceChar are simply aliases for char. There is no difference between bar<char> and bar<myChar>
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users