Overloading operator-* ?

Started by
3 comments, last by MaulingMonkey 18 years, 10 months ago
I've decided to create a stab at doing some library designing, starting with a clone_ptr class which allows for polymorphic types yet deep-copies (via clone() function of a template argument, cloner_type). Unfortunately, I've run into a minor stumbling block/design issue with the class. In creating my usage case, I set up this:
std::vector< industry::clone_ptr< animal > > animals;
//fill animals
std::for_each( animals.begin() , animals.end() , std::mem_fun( &animal::noise ) );
Unfortunately, this dosn't work without an implicit cast to pointer, which I'd really rather not allow. Right now I'm banging up an industry::mem_fun_ptr which allows this behavior by accepting a templatized operator() argument rather than only normal pointers. For maximum flexibility, I'd like the functon returned (mem_fun_ptr_t) to use operator->* as per normal usage with pointers. However, I appear not to posess the knowledge to overload them :-). According to at least some sources, ->* can be overloaded, as a unary operator. I tried overloading it as one would operator->, as per so:
50		pointer_type operator->*( void ) {
			return pointer;
		}
53		const_pointer_type operator->*( void ) const {
			return pointer;
		}
However, I get these errors:
C:/eclipse/workspace/libindustry/industry/clone_ptr.hh:50: error: `value_t* industry::clone_ptr<value_t, cloner_t>::operator->*()' must take exactly one argument
C:/eclipse/workspace/libindustry/industry/clone_ptr.hh:53: error: `const value_t* industry::clone_ptr<value_t, cloner_t>::operator->*()' must take exactly one argument
C:/eclipse/workspace/libindustry/industry/clone_ptr.hh: In instantiation of `industry::clone_ptr<animal, industry::cloner<animal> >':
../main.cc:36:   instantiated from here
Needless to say this has me rather confused - a unary operator should take zero arguments, right? So why is GCC (3.4.2) complaining about arguments, rather than, say, return types? Currently, my usage case is completely working aside from this fact, and i can workaround like so:
	for_each
		( animals.begin()
		, animals.end()
		, compose1
			( mem_fun( &animal::noise )
			, mem_fun_ref( &clone_ptr< animal >::get )
			)
		);
But that's just plain ugly :-P. Offtopic: Yes, I'm aware std::compose1 is an SGI extension - but I'm not using it (SGI's STL not installed) - I implemented industry::compose1, which is what's being used, outside of the std:: namespace, where it belongs ^_^. Edit: It would appear that operator->* is actually a binary operator. I didn't believe The MSDN, since it says that about operator-> as well, but considering that GCC dosn't choke on the function decleration when I do stuff like this:
		template < typename argument_type >
		argument_type operator->*( argument_type (value_type::*argument) ) {
			return (pointer->*argument);
		}
I'd say I'm on the right track - kind of maybe sorta. This still dosn't work:
clone_ptr< animal > test_mutant;
test_mutant = test_duck;
void (animal::*function)( void ) const = &animal::noise;
(test_mutant->*function)(); //error: no match for 'operator->*' in 'test_mutant ->* function'
[Edited by - MaulingMonkey on May 26, 2005 1:35:38 PM]
Advertisement
Quote:Original post by MaulingMonkey
According to at least some sources, ->* can be overloaded, as a unary operator. I tried overloading it as one would operator->, as per so:

50		pointer_type operator->*( void ) {			return pointer;		}53		const_pointer_type operator->*( void ) const {			return pointer;		}


However, I get these errors:

C:/eclipse/workspace/libindustry/industry/clone_ptr.hh:50: error: `value_t* industry::clone_ptr<value_t, cloner_t>::operator->*()' must take exactly one argumentC:/eclipse/workspace/libindustry/industry/clone_ptr.hh:53: error: `const value_t* industry::clone_ptr<value_t, cloner_t>::operator->*()' must take exactly one argumentC:/eclipse/workspace/libindustry/industry/clone_ptr.hh: In instantiation of `industry::clone_ptr<animal, industry::cloner<animal> >':../main.cc:36:   instantiated from here


Needless to say this has me rather confused - a unary operator should take zero arguments, right? So why is GCC (3.4.2) complaining about arguments, rather than, say, return types?


Because you are declaring a global overloaded operator *, you need to pass in the object being dereferenced.

50		pointer_type operator->*( obj &o ) {			return o->pointer;		}53		const_pointer_type operator->*( obj &o ) const {			return o->pointer;		}


My code probably didn't make much sense because I didn't thoroughly read what type of objects you're working with. But that's the gist of it- you need to pass in the object being worked on.

If it was in a class, you could just do

50		pointer_type obj::operator->*( ) {			return this->pointer;		}53		const_pointer_type obj::operator->*( ) const {			return this->pointer;		}


and there would be no arguments there because it would autmatically be the instance of the class.

I'm pretty sure that's your error.
Sorry, I should have specified - both those functions are within the clone_ptr class body, just like operator->.

After sifting through many pages of google search (since even quoting "operator ->*" dosn't make it recognize I'm looking specifically for that string ~_~) I found this, which makes it sound like ->* is indeed a binary operator that should return a functor... I'm not exactly sure how this works alongside pointer-to-member-data s, but I'll post again once I try the functor return...

EDIT: After dealing with a few annoying, somewhat tricky to notice without my caffinee bugs (with one value_type obscuring the other... renamed to the more appropriate result_type soas not to interfere) I've finally gotten it working.

Here's the current member functions I'm using (no less than 6 - pointer-to-member-variable, pointer-to-member-0-argument-function, and pointer-to-member-1-argument-function, plus const version).

		//operator->* for pointer-to-member-variable		template < typename argument_type >		argument_type & operator->*( argument_type (value_type::*argument) ) {			return (pointer->*argument);		}		template < typename argument_type >		const argument_type & operator->*( argument_type (value_type::*argument) ) const {			return (pointer->*argument);		}				//operator->* for pointer-to-member-0-argument-function		template < typename result_type >		boost::function< result_type ( void ) > operator->*( result_type (value_type::*function)( void ) ) {			return boost::bind( function , pointer );		}				template < typename result_type >		boost::function< result_type ( void ) > operator->*( result_type (value_type::*function)( void ) const ) const {			return boost::bind( function , pointer );		}				//operator->* for pointer-to-member-1-argument-function		template < typename result_type , typename argument_type >		boost::function< result_type ( argument_type ) > operator->*( result_type (value_type::*function)( argument_type ) ) {			return boost::bind( function , pointer );		}				template < typename result_type , typename argument_type >		boost::function< result_type ( argument_type ) > operator->*( result_type (value_type::*function)( argument_type ) const ) const {			return boost::bind( function , pointer );		}


Tested via test_mutant->*(&animal::noise); and this now correctly works:

	for_each( animals.begin() , animals.end() , mem_fun_ptr( &animal::noise ) );


Drawbacks: only works for 0/1 argument functions, I'll have to see if I can't hack out something using boost for N-argument functions (prehaps today is a good day to learn Boost.Preprocessor....

[Edited by - MaulingMonkey on May 26, 2005 2:01:52 PM]
...

Can't you just make Animal::noise() accept a clone_ptr<Animal> instead of an Animal*? o_O
Quote:Original post by Zahlman
...

Can't you just make Animal::noise() accept a clone_ptr<Animal> instead of an Animal*? o_O


Eh? animal * is the "this" parameter - If you mean make animal::noise() a member (stub) function of clone_ptr... clone_ptr is meant to be as generic as possible, so making noise() a stub member function of it is unacceptable.

By just overloading operator->, we can allready do:
clone_ptr< animal > my_cow( new cow() );my_cow->noise();
However, we run into compile errors when we attempt:
void (animal::*function)( void ) const = &animal::noise;my_cow->*function();
This kind of behavior is relied on by std::mem_fun to call it's constructor-argument, and it works well with raw pointers:
vector< animal * > animals;for_each( animals.begin() , animals.end() , std::mem_fun( &animal::noise ) );
It dosn't work so well with user defined types though:
vector< clone_ptr< animal > > animals;for_each( animals.begin() , animals.end() , std::mem_fun( &animal::noise ) );
This is firstly because std::mem_fun_t::operator() only accepts pointers. Secondly, it's also because I didn't have operator->* defined.

By implementing it in my own functor, industry::mem_fun_ptr_t, and wrapper function industry::mem_fun_ptr, I was able to get around the first problem - but in order to make this work:
for_each( animals.begin() , animals.end() , industry::mem_fun_ptr( &animal::noise ) );
I had to define operator->* such that it'd work.

This topic is closed to new replies.

Advertisement