The joy (?) of C++98 argument forwarding

  • Thread starter Alf P. Steinbach
  • Start date
A

Alf P. Steinbach

Consider ...

<code>
// Copyright (c) Alf P. Steinbach, 2010.
#include "_config.h"

#include <progrock/cppx/pointers/Shared.h>
#include <iostream>

namespace {
using namespace progrock;

cppx::Size count = 0;

class Foo
{
protected:
~Foo()
{
using namespace std;
--count;
cout << "Foo destroyed" << endl;
}

public:
Foo()
{
using namespace std;
cout << "Foo constructed" << endl;
++count;
}

Foo( int x )
{
using namespace std;
cout << "Foo constructed with arg " << x << endl;
++count;
}

Foo( int x, char const s[] )
{
using namespace std;
cout << "Foo constructed with args " << x << " and \"" << s << "\""
<< endl;
++count;
}
};

void test()
{
using namespace cppx;

Shared< Foo > r1 = newObject();
assert( count == 1 );

Shared< Foo > r2( r1 );
assert( count == 1 );

Shared< Foo > r3( newObject(), args( 42 ) );
assert( count == 2 );

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );
assert( count == 3 );

r4 = r2;
assert( count == 2 );

r3 = r2;
assert( count == 1 );

r3 = r3;
assert( count == 1 );
}
}; // namespace anon

void testShared()
{
test();
assert( count == 0 );
}
</code>


Is the notation in the 'test' routine OK, or would it be better (in some sense)
with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?


Cheers,

- Alf (notation-aware)
 
D

DeMarcus

Alf said:
Consider ...

<code>
// Copyright (c) Alf P. Steinbach, 2010.
#include "_config.h"

#include <progrock/cppx/pointers/Shared.h>
#include <iostream>

namespace {
using namespace progrock;

cppx::Size count = 0;

class Foo
{
protected:
~Foo()
{
using namespace std;
--count;
cout << "Foo destroyed" << endl;
}

public:
Foo()
{
using namespace std;
cout << "Foo constructed" << endl;
++count;
}

Foo( int x )
{
using namespace std;
cout << "Foo constructed with arg " << x << endl;
++count;
}

Foo( int x, char const s[] )
{
using namespace std;
cout << "Foo constructed with args " << x << " and \"" << s
<< "\"" << endl;
++count;
}
};

void test()
{
using namespace cppx;

Shared< Foo > r1 = newObject();
assert( count == 1 );

Shared< Foo > r2( r1 );
assert( count == 1 );

Shared< Foo > r3( newObject(), args( 42 ) );
assert( count == 2 );

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );
assert( count == 3 );

r4 = r2;
assert( count == 2 );

r3 = r2;
assert( count == 1 );

r3 = r3;
assert( count == 1 );
}
}; // namespace anon

void testShared()
{
test();
assert( count == 0 );
}
</code>


Is the notation in the 'test' routine OK, or would it be better (in some
sense) with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?

At least consider CPPX_NEW_SHARED_FOO( 42, "blah blah" ). Otherwise the
macro name would be as non-intuitive as

CPPX_NEW_SHARED( PI, 21.45, time() )

Can't you do the wrapper with a template instead of a macro?

/Daniel
 
A

Alf P. Steinbach

Alf said:
Consider ...

<code>
// Copyright (c) Alf P. Steinbach, 2010.
#include "_config.h"

#include <progrock/cppx/pointers/Shared.h>
#include <iostream>

namespace {
using namespace progrock;

cppx::Size count = 0;

class Foo
{
protected:
~Foo()
{
using namespace std;
--count;
cout << "Foo destroyed" << endl;
}

public:
Foo()
{
using namespace std;
cout << "Foo constructed" << endl;
++count;
}

Foo( int x )
{
using namespace std;
cout << "Foo constructed with arg " << x << endl;
++count;
}

Foo( int x, char const s[] )
{
using namespace std;
cout << "Foo constructed with args " << x << " and \"" << s << "\"" <<
endl;
++count;
}
};

void test()
{
using namespace cppx;

Shared< Foo > r1 = newObject();
assert( count == 1 );

Shared< Foo > r2( r1 );
assert( count == 1 );

Shared< Foo > r3( newObject(), args( 42 ) );
assert( count == 2 );

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );
assert( count == 3 );

r4 = r2;
assert( count == 2 );

r3 = r2;
assert( count == 1 );

r3 = r3;
assert( count == 1 );
}
}; // namespace anon

void testShared()
{
test();
assert( count == 0 );
}
</code>


Is the notation in the 'test' routine OK, or would it be better (in
some sense) with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?

At least consider CPPX_NEW_SHARED_FOO( 42, "blah blah" ). Otherwise the
macro name would be as non-intuitive as

CPPX_NEW_SHARED( PI, 21.45, time() )

Well for the example above the type is implicit in the declaration, so it would
be redundant to repeat it there.

On the other hand you have a point -- I didn't think of it -- for an actual
argument in a routine call.

On the third hand, we usually don't specify arguments there for value arguments.

Can't you do the wrapper with a template instead of a macro?

Not sure what you mean; the Shared shared pointer type is templated, as the
example usage code shows.


Cheers,

- Alf
 
A

Alf P. Steinbach

Alf said:
Consider ...
[snip]

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) ); [snip]

Is the notation in the 'test' routine OK, or would it be better (in
some sense) with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?

At least consider CPPX_NEW_SHARED_FOO( 42, "blah blah" ). Otherwise the
macro name would be as non-intuitive as

CPPX_NEW_SHARED( PI, 21.45, time() )

Well for the example above the type is implicit in the declaration, so
it would be redundant to repeat it there.

On the other hand you have a point -- I didn't think of it -- for an
actual argument in a routine call.

On the third hand, we usually don't specify arguments there for value
arguments.

Keyboard gremlin was active, I meant, "we usually don't specify the type for an
actual argument for a value argument".

Not sure what you mean; the Shared shared pointer type is templated, as
the example usage code shows.

Oh, I think I see what you meant now. You probably thought that the newObject()
call produces a Foo*.

It doesn't, it's just a placeholder thing, of fixed type.

In

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );

the arguments are just arguments, and it's the Shared pointer constructor that
does the new'ing.

Happily the Shared definition is not more code than I can show in Usenet article[1].

The constructor invoked by the above is the second one:


<code>
namespace progrock{ namespace cppx{

enum NewObject {};
inline NewObject newObject() { return NewObject(); }

template< typename Type >
class Shared
: protected detail::SharedBase< Type >
{
private:
typedef detail::SharedBase< Type > Base;

public:
typedef Type Object;

Shared( NewObject )
: Base( detail::newRefCounted< Object >( EmptyArgPack() ) )
{}

template< class ArgPack >
Shared( NewObject, ArgPack const& args )
: Base( detail::newRefCounted< Object >( args ) )
{}

template< class OtherType >
Shared( std::auto_ptr< OtherType > p )
: Base(
detail::newRefCounted< detail::RawPtrHolder< OtherType > >(
args( p.get() ) ),
p.get()
)
{
p.release();
}

template< class OtherType >
Shared( Shared< OtherType > const& other )
: Base( other )
{}

void swapWith( Shared& other )
{
Base::swapWith( other );
}

Shared& operator=( Shared other )
{
swapWith( other ); return *this;
}

Object* rawPtr() const { return myObject; }
Object& rawRef() const { return *rawPtr() }
Object& operator*() const { return rawPtr(); }
Object* operator->() const { return rawPtr(); }
};
} } // namespace progrock::cppx

template< class Type >
inline CPPX_IMPLEMENT_SWAP( progrock::cppx::Shared< Type > )
</code>


The question still stands, is it OK with notation like

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );

or would it be better in some sense with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?


Cheers,

- Alf

Notes:
[1] There is a subtlety here regarding copy construction. The templated
constructor that looks like a copy constructor isn't one. The compiler generated
copy constructor invokes the one of the base class.
 
A

Alf P. Steinbach

The question still stands, is it OK with notation like

Shared< Foo > r4( newObject(), args( 42, "blah blah" ) );

or would it be better in some sense with some macro, e.g. like

Shared< Foo > r( CPPX_NEW_SHARED( 42, "blah blah" ) );

?

I changed it to notation like

Shared< Foo > r1 = makeNew;
Shared< Foo > r2( r1 );
Shared< Foo > r3( makeNew, args( 42 ) );
Shared< Foo > r4( makeNew, args( 42, "blah blah" ) );
Shared< Foo > r5( std::auto_ptr< Foo >( new Foo( 0, "raw pointer" ) ) );

Is this readable/understandable?


Cheers,

- Alf

PS: I see that with Thunderbird 3.x something like OEQuoteFix (that fixed
quoting in Microsoft's Outlook Express) would be useful; I don't understand how
they could mess up everything + make the beast 100 times slower for a new
version, instead of fixing things and making it faster. Perhaps it's back to
some JavaScript and XML to cook up Yet Another TB extension, "TBQuoteFix"? Or
perhaps not: does anyone know any existing such extension?
 
D

DeMarcus

I changed it to notation like

Shared< Foo > r1 = makeNew;
Shared< Foo > r2( r1 );
Shared< Foo > r3( makeNew, args( 42 ) );
Shared< Foo > r4( makeNew, args( 42, "blah blah" ) );
Shared< Foo > r5( std::auto_ptr< Foo >( new Foo( 0, "raw pointer" ) ) );

Is this readable/understandable?

I like that much better!

The scary thing with macros is not the abstraction (that can be done
with a normal function), it's the fact that you have no idea what so
ever what the programmer has done behind the scenes. With a normal
function you now that things goes by the book, but with a macro, in
theory, anything can happen.

For example, I've seen horrible code that looked like this:

class SomeClass
{
INITIALIZE

public:
SomeClass();

};

WTF?! Am I supposed to understand the programmer's home made language?


I like your last solution.
 
A

Alf P. Steinbach

* DeMarcus:
* Alf P. Steinbach:

I like that much better!

Thanks, I'll go with it.

The scary thing with macros is not the abstraction (that can be done
with a normal function), it's the fact that you have no idea what so
ever what the programmer has done behind the scenes. With a normal
function you now that things goes by the book, but with a macro, in
theory, anything can happen.

For example, I've seen horrible code that looked like this:

class SomeClass
{
INITIALIZE

public:
SomeClass();

};

WTF?! Am I supposed to understand the programmer's home made language?

Well, sometimes the "macro language" is the most practical solution (at least as
I see it), e.g. ...


<code>
#define CPPX_IMPLEMENT_CLONING( Class ) \
virtual Class* virtualCloneThatIsUnsafeToCallDirectly() const \
{ \
assert( typeid( *this ) == typeid( Class ) ); \
return new Class( *this ); \
} \
\
::progrock::cppx::Ownership< Class > clone() const \
{ \
return ::progrock::cppx::Ownership< Class >( \
virtualCloneThatIsUnsafeToCallDirectly() \
); \
}
</code>


.... is rather difficult to do with templating and stuff. And I haven't been able
to come up with such a non-macro solution that doesn't yield very verbose and
ungrokkable client code notation. I suspect that if such a solution exists it
will rely on "clever" tricks that makes it pretty much impractical anyway.


I like your last solution.


Cheers, & thanks,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top