Click to See Complete Forum and Search --> : Compiler problem or feature?


NMTop40
March 4th, 2002, 09:54 AM
Ok, what I have tried to do here is probably one of those "reasonable-looking but faulty" uses of STL.

Here is my "sample" code which illustrates the problem;


template < typename T >
class H
{
private:
map< string, T > m_map;
public:
template< typename Predicate >
RemoveCopyIf( H< T >& rhs, Predicate pr )
{
m_map.clear();
remove_copy_if( rhs.m_map.begin(), rhs.m_map.end(),
inserter( m_map, m_map.begin() ), pr );
}
void Insert( const string& s, const T& t )
{
m_map[s] = t;
}
};

class J
{
private:
int m_x;
public:
J( int x=0 ) : m_x( x )
{
}
int x() const { return m_x; }

// keep default copy constructor and operator=
template < typename K >
static bool IsOdd( const std::pair< K, J > & kjPair )
{
return ( (kjPair.second.m_x & 1) != 0);
}
};

template < typename K >
bool IsOdd( const std::pair< K, J > & kjPair )
{
return ( ( kjPair.second.x() & 1 ) != 0 );
}


int main()
{
H< J > h1;
H< J > h2;

h1.Insert( "one", J(1) );
h1.Insert( "two", J(2) );
h1.Insert( "three", J(3) );
h1.Insert( "four", J(4) );

h2.RemoveCopyIf( h1, J::IsOdd< string > ); // internal compiler error
h2.RemoveCopyIf( h1, IsOdd< string > ); // link error if above line commented out
return 0;
}



attempting to compile with VC6

My guess is that the predicate must be a proper function pointer for the predicate and the compiler can't find one. (Is that the cause?)

btw. in my real code I used the static member function with the key filled in (non-template) after the code above failed with the internal compiler error, and I also used Nicolai M. Josuttis's asso_inserter() implementation.

I just posted this here because I'm curious (and it would have been nice to do what I was trying above)

Add the below, for example, and the above works (whether as static in the class or in global space)


bool IsOdd( const std::pair< string, J > & kjPair )
{
return ( ( kjPair.second.x() & 1 ) != 0 );
}





The best things come to those who rate

Paul McKenzie
March 4th, 2002, 11:44 AM
Here is the error when compiled with the on-line compiler at http://www.comeaucomputing.com


Thank you for testing your code with Comeau C/C++!
Your Comeau C/C++ test results are as follows:

Comeau C/C++ 4.3 BETA#2 (Feb 17 2002 22:07:43)
Copyright 1988-2002 Comeau Computing. All rights reserved.

"12412.c", line 62: error: no instance of function template
"H<T>::RemoveCopyIf [with T=J]" matches the argument list
The argument types that you used are: (H<J>, <unknown-type>)
object type is: H<J>
h2.RemoveCopyIf( h1, J::IsOdd< string > );
^

1 error detected in the compilation of "12412.c".

Tell others about http://www.comeaucomputing.com/tryitout !
In strict mode, with -tused, Compile failed
Hit the Back Button to review your code and compile options.
Go Buy Comeau C/C++!! Check out the beta for new C compiler backends

The error points to the same location as VC++. Try this on another compiler and see if it works.

Regards,

Paul McKenzie

NMTop40
March 4th, 2002, 04:57 PM
thanks for the info about the compiler but I still don't get it.

How is template function J::IsOdd<string> any different from the standalone IsOdd with string hard-coded in?

Same line as VC++ but that gave me an "internal compiler error" which means nothing.


The best things come to those who rate

Tim Tsui
March 5th, 2002, 02:23 AM
You should do like this

typedef bool (*PF)( const pair< string , J >& );
h2.RemoveCopyIf<PF>( h1, IsOdd<string> );
h2.RemoveCopyIf<PF>( h1, J::IsOdd<string> );

Tim Tsui
March 5th, 2002, 02:31 AM
The previous post cannot be compiled,
I think it has to be the following.

typedef bool (*PF)( const pair< string , J >& );
PF p = IsOdd<string>;
PF p1 = J::IsOdd;
h2.RemoveCopyIf( h1, p );
h2.RemoveCopyIf( h1, p1 );

NMTop40
March 5th, 2002, 02:44 AM
I got my code to compile by using string (hard coded into J::IsOdd - well the equivalent anyway)

How do you make it work with a template function where string is a variable type?

typedefs can make the code look neater but they don't add extra functionality.


The best things come to those who rate

Tim Tsui
March 5th, 2002, 03:01 AM
type PF has declared the key type of pair is string, so that the compiler can instantiate the template.

typedef can tell the compiler the assured type .

Paul McKenzie
March 5th, 2002, 04:32 AM
The code now also compiles with the Comeau on-line compiler with the changes made. So there must be something in typedef-ing non-trivial types if you intend to make them template arguments. Two compilers (VC++ and Comeau) coming up with the same errors, and then get corrected with the same change -- doesn't seem to be a coincidence.

BTW, the Comeau compiler adheres to the current standard much better than VC++, so there is no chance of some common compiler code being shared.

Regards,

Paul McKenzie

NMTop40
March 5th, 2002, 05:12 AM
which surprises me. I know sometimes typedef'ing fixes problems on VC++ but didn't expect it to be part of a "standard".

I assume Tim Tsui meant that the typedefs should be made in the local function (main in the example), so that J can still have the static template member function with a general key type.


The best things come to those who rate

NMTop40
March 5th, 2002, 06:28 AM
(it still failed with internal compiler error in VC++ though )




The best things come to those who rate

Paul McKenzie
March 5th, 2002, 12:21 PM
Hmm...I have Borland C++ Builder 5.0 at home. Maybe I'll try it there to see what it says. So far, only Comeau handles the typedef solution.

Regards,

Paul McKenzie

Tim Tsui
March 5th, 2002, 08:22 PM
h2.RemoveCopyIf( h1, &IsOdd<string> );
h2.RemoveCopyIf( h1, &J::IsOdd<string> );
The above can be compiled under gcc.
While under SGI CC , these must be
typedef bool (*PF)( const pair< string , J >& );
h2.RemoveCopyIf<PF>( h1, IsOdd<string> );
h2.RemoveCopyIf<PF>( h1, J::IsOdd<string> );

In VC6 , they have to be
typedef bool (*PF)( const pair< string , J >& );
PF p = IsOdd<string>;
PF p1 = J::IsOdd;
h2.RemoveCopyIf( h1, p );
h2.RemoveCopyIf( h1, p1 );


It seems a templat argument of a function must be deduced from a call in VC , while it can be given in SGI CC and gcc.

cpitis
March 6th, 2002, 01:32 AM
The internal compiler error occures very often, when there is some syntax error in template class definitions. Unfortunately, I cannot say what's wrong in this code without trying it.

It is like the static method declaration is not what VC++ compiler likes. If you modify not to be template, it works:


static bool IsOdd( const std::pair< string, J > & kjPair )
{
return ( (kjPair.second.m_x & 1) != 0);
}




Your static method implementation may be considered as partial template specialization, which, unfortunately, is not supported by VC.

NMTop40
March 6th, 2002, 11:31 AM
by using a functor class instead of a static template member function. I did, however, have a problem trying to make the nested template class a friend (so it calls a public method)


template < typename T >
class H
{
private:
map< string, T > m_map;
public:
template< typename Predicate >
void RemoveCopyIf( H< T >& rhs, Predicate pr )
{
m_map.clear();
remove_copy_if( rhs.m_map.begin(), rhs.m_map.end(),
inserter( m_map, m_map.begin() ), pr );
}
void Insert( const string& s, const T& t )
{
m_map[s] = t;
}
};

class J
{
private:
int m_x;
public:
J( int x=0 ) : m_x( x )
{
}
int x() const { return m_x; }

// keep default copy constructor and operator=

template < typename K >
struct IsOdd
{
bool operator() ( const std::pair< K, J > & kjPair )
{
return ( (kjPair.second.x() & 1) != 0);
}
};
};

int main()
{
H< J > h1;
H< J > h2;

h1.Insert( "one", J(1) );
h1.Insert( "two", J(2) );
h1.Insert( "three", J(3) );
h1.Insert( "four", J(4) );

h2.RemoveCopyIf( h1, J::IsOdd< string >() );
return 0;
}



This compiles fine on Visual C++, althugh I haven't tried running it outputting
the results (of h2)

(Anyone resolve how I could make the inner template class a friend of J ? )


The best things come to those who rate

NMTop40
March 7th, 2002, 02:28 AM
I got it to work in the end by using a nested template functor class instead of a function. I haven't yet worked out how to make this class a friend. I tried

template < typename K > friend J::IsOdd< K >;



but got a compiler error.


The best things come to those who rate

cpitis
March 7th, 2002, 02:46 AM
Unfortunately, there is no way of declaring a template class as a friend of another class. Something like this is not allowed:

class A
{
template <typename T> friend class B;
};




I'm not sure, but try something like (if I remember well, IsOdd should be a friend of class J, and is declared inside the class J):


friend typename IsOdd;

NMTop40
March 7th, 2002, 04:07 AM
The thing you said is not allowed is used in boost's shared pointers. However they have disabled it for many compilers, including Visual C++.

It seems the best solution is to ensure the functor only calls public methods. There is another workaround, which I don't like, and that is to make the member private in the inner class and make the friendship the other way. Then the outer class will need an instance of the inner class, (maybe with K=void or void*/int if it won't compile with void)


The best things come to those who rate

cpitis
March 7th, 2002, 04:14 AM
I think avoiding using friend classes and function is a good strategy.

I work with Visual C++ and I know there are some standard things not supported. Some missing features made me write some equivalent ugly code (for example, partial template specialization).

NMTop40
March 7th, 2002, 04:14 PM
in general I prefer not to use friends, but I often use them for inner (nested)classes because that does not lead to any more dependencies than were there before.

Usually the inner class is there to implement some feature of the class (as in my case) and is therefore a relevant part of it.

Similarly, you may want the inner class to be public to the outside, but only in part. However you may want your outer class to have access to the full detail. If you put in public access functions, others can call them just as well as your outer class.

By the way, a lot of people's favourite model is faulty:


class MyInterfaceImpl;
//
class MyInterface
{
public:
// all my public functions here
private:
MyInterfaceImpl * m_pImpl;
};




This is faulty because generally m_pImpl is created with new in the constructor and deleted with delete in the destructor.

In that case, you have to either put in a deep copy constructor and assignment operator or disable them altogether by making them private.


The best things come to those who rate