C vs. C++

J

jacob navia

Keith said:
jacob navia said:
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.
 
I

Ian Collins

jacob said:
Keith said:
jacob navia said:
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.

Something like:

template <typename R, typename LHS, typename RHS>
R min( R& r, const LHS& lhs, const RHS& rhs )
{
return r = (lhs>rhs) ? rhs : lhs;
}

would be a start.
 
I

Ian Collins

Kaz said:
Generic algorithms over user-defined types requires only one thing:
user defined generic methods.

Overloaded operators are just syntactic sugar to connect operator
syntax with functions. They are not what makes the generic
algorithm implementation possible.
True enough, but like most syntactic sugar they do make them more elegant.
 
J

jacob navia

Ian said:
jacob said:
Keith said:
[...]
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?
Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.

Something like:

template <typename R, typename LHS, typename RHS>
R min( R& r, const LHS& lhs, const RHS& rhs )
{
return r = (lhs>rhs) ? rhs : lhs;
}

would be a start.

You can't do that. "min" i already in the C++ standard library.
 
I

Ian Collins

jacob said:
Ian said:
jacob said:
Keith Thompson wrote:
[...]
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.

Something like:

template <typename R, typename LHS, typename RHS>
R min( R& r, const LHS& lhs, const RHS& rhs )
{
return r = (lhs>rhs) ? rhs : lhs;
}

would be a start.

You can't do that. "min" i already in the C++ standard library.

You can. std::min is the standard algorithm, my suggestion isn't in the
standard library's namespace.
 
P

Phil Carmody

Ian Collins said:
jacob said:
Ian said:
jacob navia wrote:
Keith Thompson wrote:
[...]
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.

Something like:

template <typename R, typename LHS, typename RHS>
R min( R& r, const LHS& lhs, const RHS& rhs )
{
return r = (lhs>rhs) ? rhs : lhs;
}

would be a start.

You can't do that. "min" i already in the C++ standard library.

You can. std::min is the standard algorithm, my suggestion isn't in the
standard library's namespace.

You forget, we're in c.l.c here. In c.l.c, we accept prior (including
de facto) versions of the standard as being valid for discussion. By
those rules, we'd have to accept prior (de facto) C++ standards (of
which there have been many) into discussions of C++. Consequently you
cannot assert that "std::min" is the standard algorithm, and that
"min" isn't reserved. If you wish to play some other pedantic C++
standards game, please take the thread to somewhere, erm, perhaps,
that was to do with, like, you know, erm, C++, rather than C?

Yours K&R-ily,
Phil
 
C

CBFalconer

REH said:
.... snip ...

There are a lot of things C++ has to improve portability. For
example, unlike C, C++ has a portable mechanism for comparing
unrelated pointers. The next version of the standard will include
portable mechanisms for doing such things as memory fences and
atomic operations.

C can do that also, with an equal/notequal comparison guaranteed.
What it can't do is compare pointers for relative position, if both
pointers are not to areas in a single object. This is caused by
the fact that there are NO restrictions on where to get memory.
Thus pointers can include a lot more information than just the
offset in some area.
 
C

CBFalconer

Kaz said:
.... snip ...


First, we'd get together and discuss the semantics of what we
mean by min and max routines over angles. What are the issues
and what are the requirements. We would then think about how to
represent angles, etc.

For example, is 58 PI equal to 56 PI, etc. Is 57.99 PI less that
56 PI? Greater? Your template won't be much use unless the
underlying conditions are immediately obvious to the user.
 
C

CBFalconer

jacob said:
.... snip ...


You can't do that. "min" i already in the C++ standard library.

You mean the C std library. C++ has different rules. The problem
is that this thread is discussing C++ in a C newsgroup.
 
J

James Kuyper

CBFalconer said:
REH wrote:
... snip ...

C can do that also, with an equal/notequal comparison guaranteed.
What it can't do is compare pointers for relative position, if both
pointers are not to areas in a single object.

Yes, that is precisely what REH presumably meant to refer to;
 
R

REH

For example, is 58 PI equal to 56 PI, etc.  Is 57.99 PI less that
56 PI?  Greater?  Your template won't be much use unless the
underlying conditions are immediately obvious to the user.

But that is the point I am trying (poorly) to get across. The template
does not dictate this!

REH
 
C

CBFalconer

James said:
Yes, that is precisely what REH presumably meant to refer to;

For example, imagine a system where, after writing 0 to ioport 5,
memory addresses 1000 to 2000 address memory on one chip. After
writing 1 to ioport 5, those addresses reach another chip. There
is NO way to define greater or smaller addresses between the
chips. Yet pointers have to be able to access both!

So there can't be a portable method of ordering pointers. Maybe
C++ has some sort of provision that excludes such a mechanism. C
doesn't, to my knowledge.
 
R

REH

For example, imagine a system where, after writing 0 to ioport 5,
memory addresses 1000 to 2000 address memory on one chip.  After
writing 1 to ioport 5, those addresses reach another chip.  There
is NO way to define greater or smaller addresses between the
chips.  Yet pointers have to be able to access both!

So there can't be a portable method of ordering pointers.  Maybe
C++ has some sort of provision that excludes such a mechanism.  C
doesn't, to my knowledge.

C++ does indeed define a portable method to determine ordering among
unrelated pointers. Your example assumes a one-to-one map between
addresses and pointers. Neither language requires this. I don't know,
nor do I care, how C++ accomplishes this. It just does.

REH
 
K

Kaz Kylheku

In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides.

An overloaded function, or template function, is not constrained to use the
same type for all parameters.

The real problem is that the template cannot instantiate a return type based on
the types of the arguments.

You cannot encode your own custom arithmetic promotion rules. The template
cannot analyze its instantiated arguments and decide, ``aha, we have a double X
int combination here, so the int side promotes to double and the return type is
double''.

The return type is either fixed, or a template parameter. If it's a template
parameter, it must appear in template function's argument list (but we could
work around this restriction by making max a template class).

If you use a macro, it inherits the promotion rules from the build-in language
construct, so you win there. But you're also bringing in multiple evaluation of
the arguments.

This does indicate indicates that templates have some shortcomings. A template
function is not the right tool for implementing max.

But show me your max preprocessor macro that avoids multiple evaluation of A
and B, takes only the values as parameters (no type name hints) that can be
called using one name for all combinations, and doesn't use any GNU C or other
extensions like ``typeof''.
If you write:
max(2,3.5)
the template solution will not work.

You are better off writing a set of overloaded functions, where you can craft
the return value by hand. See below.
This means that a colleague of mine is working since 2-3 weeks making
hundreds of modifications to a huge C++ code base. Making max() a
template was a BIG MISTAKE.

Obviously, the template-based max, limited as it is, worked in these hundreds
of places before! Your colleague ran into a snag and is now retargetting code.

But, even if you make max a template, it should be easy to revert it to a set
of overloads.

Your code should just be calling max(x, y), allowing the template to deduce and
expand the instantiations.

You can throw the template out and hand-roll a bunch of overloads for max.

#include <cstdio>

#define MAX_IMPL(a, b) ((a) > (b) ? (a) : (b))

double max(double a, int b)
{
return MAX_IMPL(a, b);
}

double max(int a, double b)
{
return MAX_IMPL(a, b);
}

int max(int a, int b)
{
return MAX_IMPL(a, b);
}

int main()
{
double m1 = max(3.0, 4);
double m2 = max(3.0, 2);
double m3 = max(3, 4.0);
double m4 = max(3, 2.0);
int m5 = max(5, 3);

printf("%f %f %f %f %i\n", m1, m2, m3, m4, m5);
}


Output:

4.000000 3.000000 4.000000 3.000000 5

I can't imagine what kind of a mess your colleagues could have created with a
template-based max() that couldn't be cured like this. Maybe the code didn't
rely on template argument deduction, so it was littered with explicit template
argument lists that must now be gone? What was the actual issue?

On the other hand, I used a macro as a labor-saving device above; it's not
intended to be used by everyone. The MAX_IMPL macro in my code cannot be
replaced by a regular function (that leads us to regress, since we are trying
to use it to implement a function). The MAX_IMPL macro cannot be replaced by
single template function, because of variance in the return value. There is no
way in standard C++ of avoiding repetition of (a > b ? (a) : (b)) which is more
convenient than a preprocessor macro.
 
K

Kaz Kylheku

jacob navia said:
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)

That's no good. A suitable overload using max functions will handle
the conversion for you.
or, more generally,
max((double)int_expr, double_expr)

But that does not behave as smoothly as a built-in oeprator such as +.

When adding numbers, you don't have to write:

(double) 2 + 3.5

If the programming language extension feature (like templates) cannot
give you seamless behavior like this, it's lacking.
Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Such a case would occur in some other template which uses that template.

Doh?

Polymorphism can use other polymorphism.

Suppose you have a template in which there are two different types T and S,
denoting some variables T x and S y. You want to call max(x, y).
Since it's a template, you don't know.

Also how do I write this template:

template <typename LEFT, typename RIGHT>
XXX max(const LEFT &left, const RIGHT &right)
{
// ...
}

What C++ syntax can we substitute in the place indicated by XXX which
will do this: if LEFT is double and RIGHT is int, it will produce double.
If LEFT is int but RIGHT is double, it will produce double.
If both are int, it will produce int. Et cetera.

I want to be able to call a compile-time function in the template
which will compute the resulting type based on arbitrary rules,
and substitute that throughout the template expansion.
 
I

Ian Collins

Phil said:
Ian Collins said:
jacob said:
Ian Collins wrote:
jacob navia wrote:
Keith Thompson wrote:
[...]
In the company I am working we are trying to replace all calls to the
max() macro with a call to our own macro since the template solution
requires identical types at both sides. If you write:
max(2,3.5)
the template solution will not work.
[...]

It will work just fine if you write
max(2.0, 3.5)
or, more generally,
max((double)int_expr, double_expr)

Yes, it's a little more typing. Are you concerned about cases where
you don't know, at the point of call, what the types are and what type
one or both of them should be converted to?

Of course!

I mean obviously I can make that cast, but in most cases either we do
not know what type of both variables are or it is difficult to figure
out the type because of inheritance, templates and what have you!

And there are hundreds of those max(a,b) around.
Something like:

template <typename R, typename LHS, typename RHS>
R min( R& r, const LHS& lhs, const RHS& rhs )
{
return r = (lhs>rhs) ? rhs : lhs;
}

would be a start.

You can't do that. "min" i already in the C++ standard library.
You can. std::min is the standard algorithm, my suggestion isn't in the
standard library's namespace.

You forget, we're in c.l.c here. In c.l.c, we accept prior (including
de facto) versions of the standard as being valid for discussion.

Don't forget Trolls, no thread here is complete without mention of those.
 
P

Phil Carmody

CBFalconer said:
For example, imagine a system where, after writing 0 to ioport 5,
memory addresses 1000 to 2000 address memory on one chip. After
writing 1 to ioport 5, those addresses reach another chip. There
is NO way to define greater or smaller addresses between the
chips. Yet pointers have to be able to access both!

There is a way. In order to uniquely identify that memory location,
the '0' and '1' that are most recently written to the port are part
of the address. Otherwise 1234 in bank 0 and 1234 in bank 1 would
be compared equal, which they mustn't be. How this extra bit is
included in the numeric value being compared is arbitrary (and thus
intrinsically non-portable, as you say (snipped)).

Phil
 
M

Mark Wooding

Tomás Ó hÉilidhe said:
The thing about the extra features of C++ is that you can be oblivious
to them. Take a C programmer and just tell him to do a handful of
stuff such as not use "class" as an identifier, and they're good to
go. They can use their C++ compiler as a C compiler.

Dangerous codswallop. C++ exceptions get dragged in by innocuous
features like `new', and then you're into the business of writing
exception-safe C++, which is highly nontrivial.
The Supersoaker 6000 is the better water gun unless you have
extraneous requirements, such as needing to fit it in your school bag.

Also consider factors such as weight, which affects how quickly and
accurately you can aim it.

-- [mdw]
 
K

Kaz Kylheku

Dangerous codswallop. C++ exceptions get dragged in by innocuous
features like `new', and then you're into the business of writing
exception-safe C++, which is highly nontrivial.

You'd use new (std::nothrow) to avoid exceptions.
 
M

Mark Wooding

Kaz Kylheku said:
You cannot encode your own custom arithmetic promotion rules. The
template cannot analyze its instantiated arguments and decide, ``aha,
we have a double X int combination here, so the int side promotes to
double and the return type is double''.

I'm hardly a C++ expert, but I think you can indeed encode promotion
rules. Here's a noddy example.

// promotion is symmetric
template<typename L, typename R>
struct promote { typedef typename promote<R, L>::ty ty; };

// promotion is reflexive
template<typename T>
struct promote<T, T> { typedef T ty; };

// prefer floating to integer (example)
template<>
struct promote<int, double> { typedef double ty; };

// many others... sorry, this bit is tedious.

// now write a max which promotes properly
template<typename L, typename R>
The return type is either fixed, or a template parameter.

.... or computed from the parameters.

-- [mdw]
 

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
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top