Java vs C++, A Newbie's perspective.

J

Jeffrey Schwab

Jeffrey said:
The rough equivalent would be:

void do_something(list<Auditable*> events);

Ah! I missed the case of "events" being a list of some derived type
from Auditable, which was of course your point. For this, you have to
use something called "template templates." I don't know whether you can
specify specifically that the elements are of types derived from
Auditable right in the same line; you might have to put a (compile-time)
check in the body of the template. If you want, I'll give it a shot. :)
 
C

Chris Smith

Jeffrey Schwab said:
The rough equivalent would be:

void do_something(list<Auditable*> events);

~$ cat main.cpp
#include <list>
using namespace std;

class Auditable { };
class Event1 : public Auditable { };
class Event2 : public Auditable { };

void do_something(list<Auditable *> events);

int main(int argc, char *argv)
{
list<Event1 *> e;
do_something(e);

return 0;
}
~$ g++ main.cpp
main.cpp: In function `int main(int, char*)':
main.cpp:13: error: conversion from `std::list<Event1*, std::allocator
<Event1*>>' to non-scalar type `std::list<Auditable*, std::allocator
This can be done in pretty much the same way as the do_something()
example, but it's typically not necessary.

Since the do_something() example didn't work, I suppose that doesn't
bode well for other things done in pretty much the same way.
If you just use the
necessary features of T in the body of your template, the compiler will
choose the right code for you automatically.

Yes, absolutely; which makes templates a great mechanism for
abbreviating and abstracting code, but not for more powerful type
checking.
Some checks are done before the expansion (unlike macros), but others
are done afterward. That part actually gets complicated.

You seem to have assumed a rather narrow definition of "macro". That
explains some of the confusion here. I did not mean to imply such a
narrow definition of a macro. Nevertheless, no checks that are
interesting in terms of the expressive power of the type system are
performed prior to macro expansion. Templates allow all sorts of
interesting evaluation to occur within the compiler (an entire Turing
complete language, in fact), but very little additional expressive
power.
Not sure I understand the word "subsumption" in this context. :(

It means that given:

class Dog { public: void run(); };
class Thread { public: void run(); };

and:

class Scheduler<T> { ... t.run(); ... };

it is just as valid to the language to declare Scheduler<Dog> as
Scheduler<Thread>, and there's no mechanism in the language to prevent
that from occurring. The example becomes more relevant when you
consider operator overloading, but I think it's easier to understand
with plain methods. This is just an example of the minimal thing that
would be prevented by an extension to the type system to handle
reasonable constraints about parameterized types. It's not the only
possible benefit of such an exercise. If you'd like to point to others,
I'm all ears.
I'm sorry for offending you. But you really don't seem to know what
you're talking about. :(

Feel free to correct me if I'm wrong about something. So far in this
thread, I don't believe I have been wrong about anything. So far, it
appears that asking whether Java generics or C++ templates are more
powerful is much akin to asking whether the President of the United
States is more powerful than a locomotive. Depending on whose
definition of "powerful" you adopt for evaluating the question, you come
up with different answers. It further appears that you responded to my
message without taking the time to understand the sense in which
generics in Java are powerful. Understandably, then, you reached an
incorrect conclusion.
 
C

Chris Smith

Jeffrey Schwab said:
Ah! I missed the case of "events" being a list of some derived type
from Auditable, which was of course your point. For this, you have to
use something called "template templates." I don't know whether you can
specify specifically that the elements are of types derived from
Auditable right in the same line; you might have to put a (compile-time)
check in the body of the template. If you want, I'll give it a shot. :)

If you don't mind, I'd be interested in seeing it. I'm adding OT to the
subject since we clearly are.
 
T

Timo Stamm

Chris said:
I'd say that's a somewhat meaningless comparison. They have different
purposes.

On a very abstract level, they both serve the same purpose of generic
programmming. As Bjarne Stroustrup said: "we need to parameterize our
containers with the element type and do operations on parameterized
containers." That's what Java Generics do.


Timo
 
C

Chris Smith

Timo Stamm said:
On a very abstract level, they both serve the same purpose of generic
programmming. As Bjarne Stroustrup said: "we need to parameterize our
containers with the element type and do operations on parameterized
containers." That's what Java Generics do.

To me, it looks as if they only serve the same purpose on a NOT very
abstract level. Ultimately, generic collections of reference types in
Java are possible not because of generics, but because of the existence
of a universal supertype for all reference types. Generic collections
or primitive data are not possible because Java doesn't provide a
universal supertype for primitive data (neglecting boxing conversions),
and generics don't change that.

It's important for Java programmers to understand that generics in Java
are a type system feature. They do not in any way relate to what the
code does! Java's generics define a somewhat intricate language for
expressing syntactic constraints on situations in which certain code is
appropriate. Once it is determined that the code is appropriate, the
code is executed exactly as if it had not been written with generics at
all. Generics allow the programmer to limit the range of uses of some
piece of code, and then rely on the information added by these
constraints later on.

This is a huge difference from C++ templates, the whole purpose of which
is to write templates for code, which are expanded to different code
under different uses. While some of the possible expansions of
templates may differ from each other only in type checking, there are
others that differ from each other in terms of static resolution of
method calls, or even (with template specialization) completely
different structures of code. At the same time, C++ templates do NOT
provide any fundamentally new vocabulary for expressing type
constraints. Every pure type constraint on code using C++ templates is
a type constraint from the non-templated subset of the C++ language
after expansion of the templates. (I'm neglecting the type constraints
that are defined on non-type template parameters, as in
"template<int a>", because they are outside the scope of the discussion
here.) Unlike Java, in which generics augment the subtype relation with
wildcard types that allow more flexible typing of expressions, C++
templates don't introduce any type relationships that wouldn't otherwise
be there.

Clearly, it's possible to find similarities between these two features,
but I certainly wouldn't call them abstract. Instead, the similarities
seem to be that both features are used in the context of collection
classes.
 
J

Jeffrey Schwab

Chris said:
If you don't mind, I'd be interested in seeing it. I'm adding OT to the
subject since we clearly are.

Here's a generalized example, slightly modified from Bjarne Stroustrup's
FAQ:

// http://www.research.att.com/~bs/bs_faq2.html#constraints

template<class T, class B> struct Derived_from {
static void constraints(T* p) { B* pb = p; }
Derived_from() { void(*p)(T*) = constraints; }
};

template<class Container>
void draw_all(Container& c)
{
typedef typename Container::value_type T;
Derived_from<T,Shape*>(); // accept containers of only Shape*s

for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}

C++ templates are Turing-complete. They're almost a language in their
own right. They are often used to do things that otherwise are only
possibly with macros, but that's where the relationship ends. If you
want more details, check out comp.lang.c++.moderated, and if you really
want to get into it, I highly recommend the Josuttis templates book.
 
C

Chris Smith

Jeffrey Schwab said:
// http://www.research.att.com/~bs/bs_faq2.html#constraints

template<class T, class B> struct Derived_from {
static void constraints(T* p) { B* pb = p; }
Derived_from() { void(*p)(T*) = constraints; }
};

template<class Container>
void draw_all(Container& c)
{
typedef typename Container::value_type T;
Derived_from<T,Shape*>(); // accept containers of only Shape*s

for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}

Wow. That does it, all right! :) It relies on some quite clever hacks
that apply the existing type system in interesting ways, rather than
defining new extensions to the type system.
C++ templates are Turing-complete. They're almost a language in their
own right. They are often used to do things that otherwise are only
possibly with macros, but that's where the relationship ends.

To be clear here, I should point out that a macro can be (and often is)
defined as a mechanism by which one can define extensions to a language
by providing instructions by which these extensions are translated into
the subset of the language without the extension mechanism. They are a
very powerful concept, and are a staple of flexibility in languages like
LISP and Dylan. In that sense, C++ templates are definitely a kind of
macro. I am aware that C++ templates are basically Turing-complete
(except that compilers may stop processing them at 17 levels of nested
template instantiations), and this in no way interferes with their being
macros. LISP macros, after all, are defined in LISP, which is not only
Turing complete but also quite practically useful for general-purpose
programming as well.

In retrospect, I regret using the term macro when talking about C++.
The term unfortunately has a derogatory meaning in that context, mainly
because C and C++ have had a quite diminutive macro facility for many,
many years. By describing C++ templates as macros, I did not mean to
imply any of those limitations.
 
J

Jeffrey Schwab

Chris said:
Wow. That does it, all right! :) It relies on some quite clever hacks
that apply the existing type system in interesting ways, rather than
defining new extensions to the type system.

These are sort-of "hacks" in the sense of using non-intuitive language
features, but more explicit language features have been rejected by the
C++ standards committees on the grounds that they are redundant. In
practice, you rarely have to write this kind of code yourself; instead,
you use a library that wraps it nicely, e.g. boost/concept_check.
To be clear here, I should point out that a macro can be (and often is)
defined as a mechanism by which one can define extensions to a language
by providing instructions by which these extensions are translated into
the subset of the language without the extension mechanism. They are a
very powerful concept, and are a staple of flexibility in languages like
LISP and Dylan. In that sense, C++ templates are definitely a kind of
macro.

I don't know that I'd agree with that, because C++ templates don't allow
the programmer to extend the language. Certainly not as much as (say)
overloaded operators do. But I can see your opinion here.
I am aware that C++ templates are basically Turing-complete
(except that compilers may stop processing them at 17 levels of nested
template instantiations), and this in no way interferes with their being
macros. LISP macros, after all, are defined in LISP, which is not only
Turing complete but also quite practically useful for general-purpose
programming as well.

In retrospect, I regret using the term macro when talking about C++.
The term unfortunately has a derogatory meaning in that context, mainly
because C and C++ have had a quite diminutive macro facility for many,
many years. By describing C++ templates as macros, I did not mean to
imply any of those limitations.

Thanks, and sorry for my earlier tone. "Macro" in C++ context usually
means the preprocessor, and is kind of a dirty word. A lot of C
programmers try to think of templates as macro-like when they first
learn C++, and it ends up causing a lot of problems. One of the things
I think Java got right is to avoid this mess by not including such a
complicated preprocessor.
 
A

AndrewMackDonna

Jeffrey said:
Here's a generalized example, slightly modified from Bjarne Stroustrup's
FAQ:

// http://www.research.att.com/~bs/bs_faq2.html#constraints

template<class T, class B> struct Derived_from {
static void constraints(T* p) { B* pb = p; }
Derived_from() { void(*p)(T*) = constraints; }
};

template<class Container>
void draw_all(Container& c)
{
typedef typename Container::value_type T;
Derived_from<T,Shape*>(); // accept containers of only Shape*s

for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}

C++ templates are Turing-complete. They're almost a language in their
own right. They are often used to do things that otherwise are only
possibly with macros, but that's where the relationship ends. If you
want more details, check out comp.lang.c++.moderated, and if you really
want to get into it, I highly recommend the Josuttis templates book.


or
http://www.boost.org/more/generic_programming.html
 
J

Joe

Jeffrey said:
These are sort-of "hacks" in the sense of using non-intuitive language
features, but more explicit language features have been rejected by the
C++ standards committees on the grounds that they are redundant. In
practice, you rarely have to write this kind of code yourself; instead,
you use a library that wraps it nicely, e.g. boost/concept_check.

In fact this functionality for checking a type is derived from another
type was added to the ISO standard c++ library with TR1 (C++ Library
Technical Report 1 - although in the standard library it is called
is_base_of ) along with the following related type traits
functionality:
add_const · add_cv · add_pointer · add_reference · add_volatile ·
aligned_storage · alignment_of · extent · has_nothrow_assign ·
has_nothrow_constructor · has_nothrow_copy · has_trivial_assign ·
has_trivial_constructor · has_trivial_copy · has_trivial_destructor
· has_virtual_destructor · is_abstract · is_arithmetic · is_array
· is_base_of · is_class · is_compound · is_const · is_convertible
· is_empty · is_enum · is_floating_point · is_function ·
is_fundamental · is_integral · is_member_function_pointer ·
is_member_object_pointer · is_member_pointer · is_object · is_pod ·
is_pointer · is_polymorphic · is_reference · is_same · is_scalar ·
is_signed · is_union · is_unsigned · is_void · is_volatile · rank
· remove_all_extents · remove_const · remove_cv · remove_extent ·
remove_pointer · remove_reference · remove_volatile

That's quite a bit of expressive power and on top of that you have the
concept_check Jeff mentions above which checks that a type meets a set
of requirements. For example in Java if a class implements Comparable,
you can say it meets the requirements of the Comparable concept. In
concept_check you can check that any type meets the requirements for
the comparable concept without relying on inheritance and therefore you
can also use it on fundemental types, such as integers.
 
D

Dale King

I have my complaints about Java, but it sounds like your problem has
less to do with the language and more to do with not knowing the API, or
apparently reading the API documentation. Do you have a copy of the API
documentation? setLength(0) clears the contents

In general it is probably not a good idea to use setLength(0) to reuse a
StringBuffer. In the past the JDK had code to optimize conversion from a
StringBuffer to a String. The new String would actually not be a copy of
the contents of the StringBuffer, but instead they would share the same
char array. If after converting to a string you modified the
StringBuffer, then the StringBuffer would do a copy for its own use so
that it could modify it.

There would also be a problem with this sharing behavior with memory
usage. The char array in the StringBuffer can be larger than the amount
of text that it holds. You could for instance have a StringBuffer with a
char array that is several K but only holds a few characters. If you did
a toString then the String would then inherit this extra space.

By reusing the StringBuffer repeatedly you tended to accumulate extra
space as the capacity of the StringBuffer would be whatever was the
largest amount of text you had in it and you would then pass on this
bloat to the strings you created.

Looking at the source, I don't think this is the case any more in 1.5,
but is something to be aware of.
 
C

Chris Smith

Dale King said:
Looking at the source, I don't think this is the case any more in 1.5,
but is something to be aware of.

I'm looking at the source, and it looks like this hasn't changed. I
agree it's something to be aware of, and that reusing a StringBuffer
unnecessarily is not the greatest idea. That said, if there's actually
a *reason* to reuse the StringBuffer, such as if most of the content
will be remaining the same, the problem is easily avoided by use of the
new String(String) constructor.
 
D

Dale King

Chris said:
I'm looking at the source, and it looks like this hasn't changed.

It most certainly looks like it has changed to me. When you call
toString on a StringBuffer in 1.5 it makes a copy of the char array.
Prior to 1.5, the String class would not get a copy but would get the
actual char array in the StringBuffer and the StringBuffer would be
marked so that a copy needed to be made before any modification.

I think with the advent of StringBuilder in 1.5 and the gotchas that
this created they decided the optimization was no longer necessary.
I
agree it's something to be aware of, and that reusing a StringBuffer
unnecessarily is not the greatest idea. That said, if there's actually
a *reason* to reuse the StringBuffer, such as if most of the content
will be remaining the same, the problem is easily avoided by use of the
new String(String) constructor.

Actually, I don't think that would solve the issue. As I remember that
would not eliminate the wasted space. That would just give you 2 String
objects pointing at the same char array with wasted space. I believe you
actually had to use substring in order to trim the unused char array space.

If you are using 1.5 it looks like reusing StringBuffer (and more likely
StringBuilder) is not a problem. If you are using 1.4 or before you need
to be careful and understand the gotchas.

I found an old thread on this topic:

http://groups.google.com/group/comp.lang.java.programmer/browse_thread/thread/e4144e0c6ffbcfae?tvc=2
 
A

Alex

Java makes the hard stuff easy, and __the easy stuff hard.__
It's because Java is right and force you to do all in correct way. For
example, when it's possible that Exception could be thrown then you
MUST say how to handle it.
Even when you say _do nothing_ {} you must say it.

In C++ you just ignore (or may ignore/or may not) all troubles till
they appear.
Java forces you to write correct code.
C## has special key _unsafe_ to be like C++. Which makes is similar and
as bad as C++.

(I have assembler, C, C++ and Java experience for 10 years each).
Alex.
 
C

Chris Smith

Dale King said:
It most certainly looks like it has changed to me. When you call
toString on a StringBuffer in 1.5 it makes a copy of the char array.
Prior to 1.5, the String class would not get a copy but would get the
actual char array in the StringBuffer and the StringBuffer would be
marked so that a copy needed to be made before any modification.

You're right. I was confusing the (char[],int,int) constructor with the
(int,int,char[]) constructor.
Actually, I don't think that would solve the issue. As I remember that
would not eliminate the wasted space. That would just give you 2 String
objects pointing at the same char array with wasted space. I believe you
actually had to use substring in order to trim the unused char array space.

Actually, it's the opposite. The substring method does not copy the
contents but just returns a new String object pointing to the same
backing. The String(String) constructor does do the copy if the
existing char[] is too big. If you look at the code there, it
specifically checks whether the original String has a char[] that's
longer than its effective length... and if so, it allocates a different
char[] of the right size, and copies the relevant data into it.

(Notice that substring does use the String(int,int,char[]) constructor,
which does NOT copy anything.)
 

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,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top