Advice of generic interface

N

nicola

Hi,
I have a generic interface for intervals, which is defined in terms of
two global functions leftPoint() and rightPoint(). Many other
functions and operators are defined in terms of those two in a
standard way, so that, in principle, I only need to provide a
definition for leftPoint() and rightPoint() to adapt a specific data
type to an interval interface. But providing generic definitions for
the operators calls for trouble (unwanted instantiations and broken
name lookup). Is there any viable alternative to overloading all the
functions and operators for each data type (duplicating much of the
code)? Or, is there anything better than the following?

namespace A {
// Generic interface
template <typename I> point_type leftPoint(I const& i);
template <typename I> point_type rightPoint(I const& i);
template <typename I> operator<(I const& i, I const& j); // “too
generic”
// […]
// Specializations
int leftPoint(std::pair<int,int>); int
rightPoint(std::pair<int,int>);
float leftPoint(MyFloatInterval); float rightPoint(MyFloatInterval);
// […]
}
 
J

Jonathan Lee

But providing generic definitions for
the operators calls for trouble (unwanted instantiations and broken
name lookup). Is there any viable alternative to overloading all the
functions and operators for each data type (duplicating much of the
code)? Or, is there anything better than the following?

I'm not _exactly_ sure what you're trying to do, but maybe
boost::enable_if will allow you to nix templates that you
don't want ("too generic").

--Jonathan
 
J

John H.

nicola said:
Hi,
I have a generic interface for intervals, which is defined in terms of
two global functions leftPoint() and rightPoint(). Many other
functions and operators are defined in terms of those two in a
standard way, so that, in principle, I only need to provide a
definition for leftPoint() and rightPoint() to adapt a specific data
type to an interval interface. But providing generic definitions for
the operators calls for trouble (unwanted instantiations and broken
name lookup). Is there any viable alternative to overloading all the
functions and operators for each data type (duplicating much of the
code)?

I can't quite tell what your needs are, but maybe something like this:

template <typename T>
class Range
{
public:
Range(T left, T right) : left(left), right(right) { }
bool operator<(Range const & r) const
{
return left<r.left && right<r.right;
}
// other common methods go here
private:
T left;
T right;
};

You could then extend this to be initialized with certain data types
(e.g. std::pair<int,int>) by either:
Adding constructors to the class:

template <typename T>
class Range
{
public:
Range(T left, T right) : left(left), right(right) { }
Range(std::pair<int,int> vals) : left(vals.first),
right(vals.second) {} // added
bool operator<(Range const & r) const
{
return left<r.left && right<r.right;
}
// other common methods go here
private:
T left;
T right;
};

or by creating subclasses:

class IntRange : public Range<int>
{
public:
IntRange(std::pair<int,int> vals) : Range<int>(vals.first,
val.second) { }
};
 
N

nicola

I'm not _exactly_ sure what you're trying to do, but maybe
boost::enable_if will allow you to nix templates that you
don't want ("too generic").

Thanks for the suggestion, boost::enable_if seems really useful.
But let me restate my questions in different and more general terms:

1) is it bad practice or are there any drawbacks in defining a fully
generic operator within a namespace? E.g.:

namespace A {
template <typename T>
bool operator==(T const& lhs, T const& rhs) {
// Something specific to definitions in A
}
}

In all the code I've seen, operators are always overloaded for
specific types (or parametrized types, e.g., MyArray<T>), but never
for a fully generic type T.

2) Sutter and Alexandrescu suggest in C++ Coding Standards to avoid
putting a type into the same namespace as a templated function or
operator (although the STL does that). Consider the following:

namespace A {
template <typename T>
T f(T const&);

template <typename T>
void g(T const& x) {
/* … */ f(x);
}
template <typename T>
ostream& operator<<(ostream& os, T const& x) {
os << f(x);
return os;
}
// Specializations
struct X { /* … */ };
T f(X&) { /* … */ }
}

The rationale with the code above is to allow one to use f(), g() and
operator<< with different types by overloading only f() (assuming that
the implementation of g() and operator<< is common to many types). Do
Sutter and Alexandrescu imply that there may be problems with this
code? Or that g() and operator<< shouldn't be generic but there should
rather be a definition for each concrete type X?

3) According to Sutter's Interface Principle, should g() and
operator<< be considered “part of X” or not? (They do not “mention” X
as they are generic.)
 
J

Jonathan Lee

1) is it bad practice or are there any drawbacks in defining a fully
generic operator within a namespace? E.g.:

What happens if someone puts a class in A that doesn't
have a meaningful definition for operator== ? Like a
socket class or something.

If your answer happens to be "Well, nobody's gonna
add things to A" then I think _that_ would be bad
practice.

Also, how'd you even come up with a namespace-wide
definition for 'equality'? If there are particular
characteristic classes must meet to be in A, and
that then validate the use of operator==, then I'd
refer you again to enable_if. Or SFINAE in general.
[...] avoid putting a type into the same namespace
as a templated function or operator

*shrugs* Don't know about that. I've never had it be
a problem, but then I guess I don't actually do it
very much either.
The rationale with the code above is to allow one to use f(), g() and
operator<< with different types by overloading only f()

See.. this I would use SFINAE for. For example, if f()
is some kind of toString() function, then I'd simply
make it a member of X. The operator<< template would just
read

os << x.toString();
3) According to Sutter's Interface Principle, should g() and
operator<< be considered “part of X” or not? (They do not “mention” X
as they are generic.)

But once the template is instantiated then, yeah, it does
mention X. IMO that makes it "part of X"

--Jonathan
 
N

nicola

If your answer happens to be "Well, nobody's gonna
add things to A" then I think _that_ would be bad
practice.

Well, nobody is supposed to add things to namespace std, I think :)
Anyway, you've made a good point and, as others have also said,
enable_if or SFINAE is appropriate.
[…]
The rationale with the code above is to allow one to use f(), g() and
operator<< with different types by overloading only f()

See.. this I would use SFINAE for. For example, if f()
is some kind of toString() function, then I'd simply
make it a member of X. The operator<< template would just
read

  os << x.toString();

What if X is not a class? You would have to overload all the functions
and operators… The idea behind the approach I was trying is to reduce
to amount of code you would have to write for a new arbitrary type. My
goal was to adapt unrelated types to my interface without changing
them or wrapping them. But now I realize that I was doing something
pretty silly, which can be summarized as:

namespace A {
template <typename T> T f(T const&);
template <typename T> void g(T const&) { /* … */ f(x); }
template <typename T> ostream& operator<<(ostream& os, T const& x);

typedef std::pair<int,int> Y;
Y f(Y const& y) { /* … */ }
}

Then, A::g(A::Y()) would be fine (with a forward declaration), but for
operators (e.g., std::cout << A::Y()) there's no hope. Besides, this
has nothing to do with the genericity of the functions and operators.
It seems like using a base class (as another poster has suggested)
would be much easier… A “generic is-a” seems far less simple without
further support, Is boost::enable_if going to become part of the
standard?
 
P

Paul Bibbings

nicola said:
Is boost::enable_if going to become part of the standard?

The next Standard, C++0x as it is currently named, is at Final Committee
Draft. This is available online at
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf. If
you search within this document for "[meta.type.synop]" (without the
quotes), you can see that enable_if will indeed be available from the
<type_traits> header. Indeed your implementation may already have this
available, if it offers C++0x support, as well (very probably) via an
earlier implementation of `tr1', if it has that. (Don't quote me on
this, but I have the idea in my mind that, if tr1 is present for your
implementation, then Boost merely makes use of that in place of its own
implementation. Maybe.)

What compiler implementation are you using, BTW?

Regards

Paul Bibbings
 
N

nicola

nicola said:
Is boost::enable_if going to become part of the standard?

[…] you can see that enable_if will indeed be available from the
<type_traits> header.

Thanks for the pointer.
 Indeed your implementation may already have this
available

My compiler (g++ 4.2.1) has a tr1/type_traits header, but no enable_if
definition inside. But I've started to use boost::enable_if and that
does exactly what I want.

Nicola
 

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,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top