468,241 Members | 1,571 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,241 developers. It's quick & easy.

passing ref to ptr again as ref to ptr....

Let's say I have this code

--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)

};

class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}

};
Why doesn't the statement in -------(2) compile?
Jun 27 '08 #1
23 1760
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
Let's say I have this code

--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)

};

class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}

};

Why doesn't the statement in -------(2) compile?

And I did have the

template<class Tin front of SpecialClass. Sorry!. I just posted a
snippet here.
Jun 27 '08 #2
os******@gmail.com wrote:
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
>Let's say I have this code

--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)

};

class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}

};

Why doesn't the statement in -------(2) compile?
Because T*& and void*& are different types with no automatic conversions
between them.
And I did have the

template<class Tin front of SpecialClass. Sorry!. I just posted a
snippet here.
Why do you run your own code for queue? It seems as though std::queue<T*>
might be what you are looking for.
Best

Kai-Uwe Bux
Jun 27 '08 #3
On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
osama...@gmail.com wrote:
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
Let's say I have this code
--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)
};
class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}
};
Why doesn't the statement in -------(2) compile?

Because T*& and void*& are different types with no automatic conversions
between them.
And I did have the
template<class Tin front of SpecialClass. Sorry!. I just posted a
snippet here.

Why do you run your own code for queue? It seems as though std::queue<T*>
might be what you are looking for.

Best

Kai-Uwe Bux
I am implementing a lockless queue, which I don't think the
std::queue<T*provides. To avoid template-induced code bloat, I am
implementing a generic the queue using void* pointers. But to gain
type-safety, I am implementing a templated interface-class.

Now what is confusing me is that this function works
SpecialQueue::Push(T* ptr) { GenericQueue::Push(ptr); }

and it converts T* to void* no problem.

But when I do

SpecialQueue::Pop(T*& refPtr) { GenericQueue::Pop(refPtr); }

Now let's say I instantiate an object

SpecialQueue<intspQ();
int a = 10;
int p = &a;
spQ.Pop(p);

It gives me this error:

"cannot convert parameter 1 from 'int *' to 'void *&'"
Jun 27 '08 #4
On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
osama...@gmail.com wrote:
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
Let's say I have this code
--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)
};
class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}
};
Why doesn't the statement in -------(2) compile?

Because T*& and void*& are different types with no automatic conversions
between them.
Sorry. Why don't we need automatic type conversion from T* to void*
but need it from T*& to void*&.
I am still learning C++, so please excuse my ignorance.
Jun 27 '08 #5
On 2008-04-25 12:32:38 -0400, os******@gmail.com said:
>
Sorry. Why don't we need automatic type conversion from T* to void*
but need it from T*& to void*&.
I am still learning C++, so please excuse my ignorance.
You need the type conversion in both cases. The difference is that the
first conversion is part of the language, and the second one is not.

void f0(void*);
void g(int *pi)
{
f0(pi); // okay: automatic conversion from int* to void*
}

void f1(void*&);
void h(int *&rpi)
{
f1(rip); // error: no conversion from int*& to void*&
}

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Jun 27 '08 #6
os******@gmail.com wrote:
On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>osama...@gmail.com wrote:
>>On Apr 24, 1:37 pm, osama...@gmail.com wrote:
Let's say I have this code
>>>--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)
>>>};
>>>class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}
>>>};
>>>Why doesn't the statement in -------(2) compile?

Because T*& and void*& are different types with no automatic
conversions between them.
>>And I did have the
>>template<class Tin front of SpecialClass. Sorry!. I just posted
a snippet here.

Why do you run your own code for queue? It seems as though
std::queue<T*might be what you are looking for.

Best

Kai-Uwe Bux

I am implementing a lockless queue, which I don't think the
std::queue<T*provides. To avoid template-induced code bloat, I am
implementing a generic the queue using void* pointers.
And you believe you can outsmart the compiler?

I wouldn't bet on that. ;-)
Bo Persson
Jun 27 '08 #7
On Apr 25, 12:48 pm, "Bo Persson" <b...@gmb.dkwrote:
osama...@gmail.com wrote:
On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
osama...@gmail.com wrote:
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
Let's say I have this code
>>--------------------
class GenericQueue
{
public:
bool Pop(void*& refToPtr); // //--------------------(1)
>>};
>>class SpecialQueue: private GenericQueue
{
public:
bool Pop(T*& refToPtr)
{
return
GenericQueue::Pop(refToPtr); //--------------------(2)
}
>>};
>>Why doesn't the statement in -------(2) compile?
Because T*& and void*& are different types with no automatic
conversions between them.
>And I did have the
>template<class Tin front of SpecialClass. Sorry!. I just posted
a snippet here.
Why do you run your own code for queue? It seems as though
std::queue<T*might be what you are looking for.
Best
Kai-Uwe Bux
I am implementing a lockless queue, which I don't think the
std::queue<T*provides. To avoid template-induced code bloat, I am
implementing a generic the queue using void* pointers.

And you believe you can outsmart the compiler?

I wouldn't bet on that. ;-)

Bo Persson
Why not?

If you instantiate GenericQueue a dozen times, you'll have a dozen
copies of its code. But if you use GenericQueue for storing void*
pointers instead, make its ctor, copy ctor, and assig operator
private, you'll prevent clients from making instances of it AND you'll
have only one version of the code. And the templated interface class
will implement its functionality in-terms-of GenericQueue
functionality and provide for type safety. The code in the interface
class is substantially less than that of the GenericQueue and will not
result in as much code bloat. No?

Of course I'll have to account for the fact that a pointer to an
object might have been pushed onto multiple stacks.

Jun 27 '08 #8
On Apr 25, 1:09 pm, osama...@gmail.com wrote:
On Apr 25, 12:48 pm, "Bo Persson" <b...@gmb.dkwrote:
osama...@gmail.com wrote:
On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>osama...@gmail.com wrote:
>>On Apr 24, 1:37 pm, osama...@gmail.com wrote:
>>>Let's say I have this code
>>>--------------------
>>>class GenericQueue
>>>{
>>>public:
>>> bool Pop(void*& refToPtr); // //--------------------(1)
>>>};
>>>class SpecialQueue: private GenericQueue
>>>{
>>>public:
>>> bool Pop(T*& refToPtr)
>>> {
>>> return
>>>GenericQueue::Pop(refToPtr); //--------------------(2)
>>> }
>>>};
>>>Why doesn't the statement in -------(2) compile?
>Because T*& and void*& are different types with no automatic
>conversions between them.
>>And I did have the
>>template<class Tin front of SpecialClass. Sorry!. I just posted
>>a snippet here.
>Why do you run your own code for queue? It seems as though
>std::queue<T*might be what you are looking for.
>Best
>Kai-Uwe Bux
I am implementing a lockless queue, which I don't think the
std::queue<T*provides. To avoid template-induced code bloat, I am
implementing a generic the queue using void* pointers.
And you believe you can outsmart the compiler?
I wouldn't bet on that. ;-)
Bo Persson

Why not?

If you instantiate GenericQueue a dozen times, you'll have a dozen
copies of its code. But if you use GenericQueue for storing void*
pointers instead, make its ctor, copy ctor, and assig operator
private, you'll prevent clients from making instances of it AND you'll
have only one version of the code. And the templated interface class
will implement its functionality in-terms-of GenericQueue
functionality and provide for type safety. The code in the interface
class is substantially less than that of the GenericQueue and will not
result in as much code bloat. No?

Of course I'll have to account for the fact that a pointer to an
object might have been pushed onto multiple stacks.
The idea is from Scott Myer's "Effective C++", Item 42 - Use Private
Inheritance Judiciously.
Jun 27 '08 #9
<os******@gmail.comwrote in message
news:39**********************************@i36g2000 prf.googlegroups.com...
: I am implementing a lockless queue, which I don't think the
: std::queue<T*provides. To avoid template-induced code bloat, I am
: implementing a generic the queue using void* pointers. But to gain
: type-safety, I am implementing a templated interface-class.

This is a common optimization technique indeed (used back in early
C++ times). But keep in mind that, nowadays, some compilers/linkers
are able to automatically eliminate redundant copies of the same
code, such as those resulting from multiple template instantiations.

: Now what is confusing me is that this function works
:
:
: SpecialQueue::Push(T* ptr) { GenericQueue::Push(ptr); }
:
: and it converts T* to void* no problem.

Indeed: the compiler passes a *copy* of ptr to the generic function.
During this copy, it can make an implicit conversion from T* to void*.

: But when I do
:
: SpecialQueue::Pop(T*& refPtr) { GenericQueue::Pop(refPtr); }
....
: It gives me this error:
:
: "cannot convert parameter 1 from 'int *' to 'void *&'"

Mind the fact that the int* and void* may have different
in-memory representations (even though this is not the case
on the common processor architectures that you and I use).
You can force the behavior you are looking for by using an
explicit cast:
SpecialQueue::Pop(T*& refPtr)
{ GenericQueue::Pop(reinterpret_cast<void*&>(refPtr) ); }
However, this will result in undefined behavior, even though
problems will only happen in "exotic" platforms. It will work
perfectly fine on most architectures, but is not portable.

In proper and portable C++, you would need to write:
bool SpecialQueue::Pop(T*& refPtr)
{
void * ptr; // I assume this is an output-only parameter
bool const result = GenericQueue::Pop(&ptr);
refPtr = static_cast<T*>(ptr); // ok, works if the
// previously pushed pointer was indeed a T* or NULL
// ( any T* can be converted to void* and then back )
return result;
}
As you see, C++ only allows the implicit conversions that are safe.
(except for those (many) that were inherited from C...)

Cheers -Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
Brainbench MVP for C++ <http://www.brainbench.com

Jun 27 '08 #10
os******@gmail.com wrote:
On Apr 25, 12:48 pm, "Bo Persson" <b...@gmb.dkwrote:
>osama...@gmail.com wrote:
>>On Apr 24, 7:14 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
osama...@gmail.com wrote:
On Apr 24, 1:37 pm, osama...@gmail.com wrote:
>Let's say I have this code
>>>>>--------------------
>class GenericQueue
>{
>public:
> bool Pop(void*& refToPtr); // //--------------------(1)
>>>>>};
>>>>>class SpecialQueue: private GenericQueue
>{
>public:
> bool Pop(T*& refToPtr)
> {
> return
>GenericQueue::Pop(refToPtr); //--------------------(2)
> }
>>>>>};
>>>>>Why doesn't the statement in -------(2) compile?
>>>Because T*& and void*& are different types with no automatic
conversions between them.
>>>>And I did have the
>>>>template<class Tin front of SpecialClass. Sorry!. I just
posted a snippet here.
>>>Why do you run your own code for queue? It seems as though
std::queue<T*might be what you are looking for.
>>>Best
>>>Kai-Uwe Bux
>>I am implementing a lockless queue, which I don't think the
std::queue<T*provides. To avoid template-induced code bloat, I
am implementing a generic the queue using void* pointers.

And you believe you can outsmart the compiler?

I wouldn't bet on that. ;-)

Bo Persson

Why not?

If you instantiate GenericQueue a dozen times, you'll have a dozen
copies of its code.
No, you will probably not.

On the compiler I use, templates will most often share code for
objects of the same size, as the linker will merge identical code
blocks. For example,

struct two
{
short x;
short y;
};

std::vector<int v1;
std::vector<long v2;
std::vector<two v3;

will only generate one set of code in the resulting .exe file.

But if you use GenericQueue for storing void*
pointers instead, make its ctor, copy ctor, and assig operator
private, you'll prevent clients from making instances of it AND
you'll have only one version of the code. And the templated
interface class will implement its functionality in-terms-of
GenericQueue functionality and provide for type safety. The code in
the interface class is substantially less than that of the
GenericQueue and will not result in as much code bloat. No?
Or it will possibly add to the code size by adding another layer. At
least it will add to the complexity of the code.
Bo Persson
Jun 27 '08 #11
Thanks Ivan and Bo.

I did not know that some compilers/linkers do in fact eliminate
redundant code. It still does not make sense to me that void* and int*
might have different size/memory representation. Aren't all pointers
indexing entries in the machine's address space? Which means a
pointer's size has to be the same regardless of what it points to
(int, void, double, ...etc.)

Jun 27 '08 #12
os******@gmail.com wrote:
Thanks Ivan and Bo.

I did not know that some compilers/linkers do in fact eliminate
redundant code. It still does not make sense to me that void* and
int* might have different size/memory representation. Aren't all
pointers indexing entries in the machine's address space? Which
means a pointer's size has to be the same regardless of what it
points to (int, void, double, ...etc.)
Yes, and no. :-)

The address space can be different than the one we see on an average
PC. If you go to some old mainframes, or some embedded processors, the
native address can be an index to a word. If you want to access
smaller parts, like characters, that may require additional info. In
that case, a char* might be quite different from an int*. This affects
the options for void*.
Bo Persson
Jun 27 '08 #13
On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
osama...@gmail.com wrote:
[...]
>I am implementing a lockless queue, which I don't think
the std::queue<T*provides.
You'd have to see the documentation for your implementation, but
by default, std::queue is lockless, since there are no locks (or
threads) in the standard. Most of the implementations I'm
familiar with do not use locks even in multithreaded
environments.

If you mean that you intent to implement a queue which can be
used without locks in a multithreaded environment, be aware that
you'll need some implementation dependent code; it can't be done
in standard C++.
>To avoid template-induced code bloat, I
am implementing a generic the queue using void* pointers.
And you believe you can outsmart the compiler?
I wouldn't bet on that. ;-)
Why not?
If you instantiate GenericQueue a dozen times, you'll have a
dozen copies of its code.
No, you will probably not.
If the types are different, and the compiler is conform, he
probably will.
On the compiler I use, templates will most often share code for
objects of the same size, as the linker will merge identical code
blocks.
I certainly hope not. That wouldn't be conform. (I presume, of
course, that the compiler will only do so if the objects are
PODs as well. Otherwise, the code won't be identical.)
For example,
struct two
{
short x;
short y;
};
std::vector<int v1;
std::vector<long v2;
std::vector<two v3;
will only generate one set of code in the resulting .exe file.
Despite the fact that the member functions of v1, v2 and v3 are
guaranteed to have different addresses?

None of the compilers I use do this (at least not to my
knowledge).
But if you use GenericQueue for storing void* pointers
instead, make its ctor, copy ctor, and assig operator
private, you'll prevent clients from making instances of it
AND you'll have only one version of the code. And the
templated interface class will implement its functionality
in-terms-of GenericQueue functionality and provide for type
safety. The code in the interface class is substantially
less than that of the GenericQueue and will not result in as
much code bloat. No?
Or it will possibly add to the code size by adding another
layer. At least it will add to the complexity of the code.
It was a more or less standard technique back when templates
first appeared, and memory was a bit scarcer. Even today, it
might be used to reduce coupling; all of the complicated parts
of the implementation are non-template, and so don't have to be
in the header. (I use it in a couple of my classes, but more
for historical reasons than anything else---the original
implementation dates back to a time when memory was scarcer. In
fact, the original implementation often dates back to
<generic.h>, when you definitely moved as much as possible out
of the "template", since the "template" was in fact an
impossible to debug macro.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #14
On Apr 27, 1:59 am, osama...@gmail.com wrote:
I did not know that some compilers/linkers do in fact
eliminate redundant code.
Very few do, and it's difficult to do it and still be standard
conform.
It still does not make sense to me that void* and int* might
have different size/memory representation.
Why not? I've worked on machines where they did, and I think
some of them are still being sold today. A void* must be able
to hold any pointer type, including a char*.
Aren't all pointers indexing entries in the machine's address
space?
I'm not sure what that means. Pointers aren't necessarily
indexes, and when they are, they don't necessarily "index"
bytes. Some common architectures, even today, have segmented
architectures, and word addressed machines still exist.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #15
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
osama...@gmail.com wrote:

* * [...]
>>I am implementing a lockless queue, which I don't think
>>the std::queue<T*provides.

You'd have to see the documentation for your implementation, but
by default, std::queue is lockless, since there are no locks (or
threads) in the standard. *Most of the implementations I'm
familiar with do not use locks even in multithreaded
environments.
And it probably is the wrong level anyway.

[snip]
>
If the types are different, and the compiler is conform, he
probably will.
On the compiler I use, templates will most often share code for
objects of the same size, as the linker will merge identical code
blocks.

I certainly hope not. *That wouldn't be conform. *(I presume, of
course, that the compiler will only do so if the objects are
PODs as well. *Otherwise, the code won't be identical.)
I believe you are wrong.
For example,
struct two
{
* * short * x;
* * short * y;
};
std::vector<int*v1;
std::vector<long*v2;
std::vector<two*v3;
will only generate one set of code in the resulting .exe file.

Despite the fact that the member functions of v1, v2 and v3 are
guaranteed to have different addresses?

None of the compilers I use do this (at least not to my
knowledge).
That must be because you do not use VC 8.0 (or 9.0). And I really do
not see any reason to disallow std::vector<int>:push_back to have an
adress that equals e.g. std::vector<unsigned>:push_back. How are you
going to detect that in the first place?

[snip]

/Peter
Jun 27 '08 #16
On 28 Apr., 10:44, James Kanze <james.ka...@gmail.comwrote:
On Apr 27, 1:59 am, osama...@gmail.com wrote:
I did not know that some compilers/linkers do in fact
eliminate redundant code.

Very few do, and it's difficult to do it and still be standard
conform.
It still does not make sense to me that void* and int* might
have different size/memory representation.

Why not? *I've worked on machines where they did, and I think
some of them are still being sold today. *A void* must be able
to hold any pointer type, including a char*.
Aren't all pointers indexing entries in the machine's address
space?

I'm not sure what that means. *Pointers aren't necessarily
indexes, and when they are, they don't necessarily "index"
bytes. *Some common architectures, even today, have segmented
architectures, and word addressed machines still exist.
And that includes the CPU most of us use to read this newsgroup with.
Intels processors have used a segmented architecture since the 286,
and are capable of adressing more than 4GB of memory. If you program
under Windows you are even able to exploit this adress space, but as I
never tried to do so, I am not aware if this is done by reintroducing
the segmented adresses space.

/Peter
Jun 27 '08 #17
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
>On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>>osama...@gmail.com wrote:

[...]
>>>>>I am implementing a lockless queue, which I don't think
>the std::queue<T*provides.

You'd have to see the documentation for your implementation, but
by default, std::queue is lockless, since there are no locks (or
threads) in the standard. Most of the implementations I'm
familiar with do not use locks even in multithreaded
environments.

And it probably is the wrong level anyway.

[snip]
>>
If the types are different, and the compiler is conform, he
probably will.
>>On the compiler I use, templates will most often share code for
objects of the same size, as the linker will merge identical code
blocks.

I certainly hope not. That wouldn't be conform. (I presume, of
course, that the compiler will only do so if the objects are
PODs as well. Otherwise, the code won't be identical.)

I believe you are wrong.
>>For example,
struct two
{
short x;
short y;
};
std::vector<intv1;
std::vector<longv2;
std::vector<twov3;
will only generate one set of code in the resulting .exe file.

Despite the fact that the member functions of v1, v2 and v3 are
guaranteed to have different addresses?

None of the compilers I use do this (at least not to my
knowledge).

That must be because you do not use VC 8.0 (or 9.0). And I really do
not see any reason to disallow std::vector<int>:push_back to have an
adress that equals e.g. std::vector<unsigned>:push_back. How are you
going to detect that in the first place?
Like James says, he can take the address of the functions and compare.
Different objects must have different addresses, including functions.

The merging of the code is really designed to solve the problem of
having vector<int>::push_back instantiated in several different
translation units. The linker tries to fix this by only keeping one
copy of each identical code block. That this also merges code blocks
for different types of equal size, is by accident.
There are other conformance problems that bother me a lot more than
this. :-)

Bo Persson
Jun 27 '08 #18
On 28 Apr., 20:22, "Bo Persson" <b...@gmb.dkwrote:
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>osama...@gmail.com wrote:
[...]
>>>>I am implementing a lockless queue, which I don't think
the std::queue<T*provides.
You'd have to see the documentation for your implementation, but
by default, std::queue is lockless, since there are no locks (or
threads) in the standard. Most of the implementations I'm
familiar with do not use locks even in multithreaded
environments.
And it probably is the wrong level anyway.
[snip]
If the types are different, and the compiler is conform, he
probably will.
>On the compiler I use, templates will most often share code for
objects of the same size, as the linker will merge identical code
blocks.
I certainly hope not. That wouldn't be conform. (I presume, of
course, that the compiler will only do so if the objects are
PODs as well. Otherwise, the code won't be identical.)
I believe you are wrong.
>For example,
struct two
{
short x;
short y;
};
std::vector<intv1;
std::vector<longv2;
std::vector<twov3;
will only generate one set of code in the resulting .exe file.
Despite the fact that the member functions of v1, v2 and v3 are
guaranteed to have different addresses?
None of the compilers I use do this (at least not to my
knowledge).
That must be because you do not use VC 8.0 (or 9.0). And I really do
not see any reason to disallow std::vector<int>:push_back to have an
adress that equals e.g. std::vector<unsigned>:push_back. How are you
going to detect that in the first place?

Like James says, he can take the address of the functions and compare.
Different objects must have different addresses, including functions.
But you compare two different types. I do know that for two objects of
the same type, if their adress compares equal, the adresses must refer
to the same object. But thus is not the same situation: you can't
directly compare a void f(int) and a void f(long). Without really
knowing (and bothering!) the standard in this respect, I am quite
confident that to compare you do need some quite heavy casting.
>
The merging of the code is really designed to solve the problem of
having vector<int>::push_back instantiated in several different
translation units. The linker tries to fix this by only keeping one
copy of each identical code block. That this also merges code blocks
for different types of equal size, is by accident.
I believe you are wrong. This certainly is not by accident as there
are different mechanisms involved. To remove different occurences of
the same function, you typically mark the function as discardable:
when during linking, the second definition of the function shows up,
you discard the new function instead of giving an error-message.
For removing different functions that generate the same code, you
probably calculate the hash-value of the code. When you find two
functions with the same hash-value, you compare the individual
opcodes: if they are similar, one function is - as above discarded.
Notice that a true duplication of code (define a non-inline function
in a header and include that header in more then one compilation unit)
still gives linker-errors.

/Peter
Jun 27 '08 #19
On Apr 28, 11:12 pm, peter koch <peter.koch.lar...@gmail.comwrote:
On 28 Apr., 20:22, "Bo Persson" <b...@gmb.dkwrote:
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
>On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>>osama...@gmail.com wrote:
>[...]
>I certainly hope not. That wouldn't be conform. (I presume, of
>course, that the compiler will only do so if the objects are
>PODs as well. Otherwise, the code won't be identical.)
I believe you are wrong.
I'm sure I'm not. The issue has been discussed before.
>>For example,
>>struct two
>>{
>>short x;
>>short y;
>>};
>>std::vector<intv1;
>>std::vector<longv2;
>>std::vector<twov3;
>>will only generate one set of code in the resulting .exe file.
>Despite the fact that the member functions of v1, v2 and v3 are
>guaranteed to have different addresses?
>None of the compilers I use do this (at least not to my
>knowledge).
That must be because you do not use VC 8.0 (or 9.0). And I really do
not see any reason to disallow std::vector<int>:push_back to have an
adress that equals e.g. std::vector<unsigned>:push_back. How are you
going to detect that in the first place?
Like James says, he can take the address of the functions
and compare. Different objects must have different
addresses, including functions.
But you compare two different types. I do know that for two
objects of the same type, if their adress compares equal, the
adresses must refer to the same object.
This is true for objects of different types as well, as long as
they are complete objects (e.g. not base classes or members of a
class or union). Except, of course, objects with different
complete types must be different objects.
But thus is not the same situation: you can't directly compare
a void f(int) and a void f(long). Without really knowing (and
bothering!) the standard in this respect, I am quite confident
that to compare you do need some quite heavy casting.
You need some casting, yes. In the case of objects, you don't,
because pointers to objects convert implicitly to void*. In the
case of pointers to function and all pointers to members, you
need some casting. But the guarantees still hold.

About the only case this might be useful in practice, I think,
is in some very advanced forms of template metaprogramming,
where you don't know the types to begin with (except that they
are e.g. pointers to a member function). And even then, I'm not
sure that it would be that useful. But the standard explicitly
guarantees it.
The merging of the code is really designed to solve the
problem of having vector<int>::push_back instantiated in
several different translation units. The linker tries to fix
this by only keeping one copy of each identical code block.
That this also merges code blocks for different types of
equal size, is by accident.
I believe you are wrong. This certainly is not by accident as
there are different mechanisms involved.
You're right about that. The first depends on "weak
references"; the linker ignores any weak references if the
extern has already been resolved. Even if the functions aren't
identical. (That could be a result of undefined behavior, but
it could also be because you compiled some of the instances with
optimization, and others without.)
To remove different occurences of the same function, you
typically mark the function as discardable: when during
linking, the second definition of the function shows up, you
discard the new function instead of giving an error-message.
For removing different functions that generate the same code,
you probably calculate the hash-value of the code. When you
find two functions with the same hash-value, you compare the
individual opcodes: if they are similar, one function is - as
above discarded. Notice that a true duplication of code
(define a non-inline function in a header and include that
header in more then one compilation unit) still gives
linker-errors.
Yes. It's also possible to arrange things so that the addresses
are different (e.g. by inserting more or less no-ops before the
function). But I've never seen a compiler which did this, and
given the size of memory today, I doubt that it's an
optimization with a particularly high priority. (It also has
the problem that it involves the linker, which means that there
can be political problems involved in implementing it.)

In the end, of course, the reason you factor out the common
behavior today is generally because you don't want complex code
to be generic (more difficult to debug and maintain), and you
definitely don't want it in a header (and most compilers still
don't implement export).

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #20
On Apr 29, 10:06 am, James Kanze <james.ka...@gmail.comwrote:
[...]
Yes. It's also possible to arrange things so that the addresses
are different (e.g. by inserting more or less no-ops before the
function). But I've never seen a compiler which did this, and
given the size of memory today, I doubt that it's an
optimization with a particularly high priority. (It also has
the problem that it involves the linker, which means that there
can be political problems involved in implementing it.)
GCC 4.3 has -frtl-abstract-sequences, but I think that currently
it doesn't work across translation units (i.e. it is a pure
compiler thing).

OTOH, in theory it does more than collapse common functions:
it can collapse any sequence of common instructions (a subset
of a function or more than one function).

--
Giovanni P. Deretta

Jun 27 '08 #21
On 29 Apr., 10:06, James Kanze <james.ka...@gmail.comwrote:
On Apr 28, 11:12 pm, peter koch <peter.koch.lar...@gmail.comwrote:
On 28 Apr., 20:22, "Bo Persson" <b...@gmb.dkwrote:
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>osama...@gmail.com wrote:
[...]
I certainly hope not. That wouldn't be conform. (I presume, of
course, that the compiler will only do so if the objects are
PODs as well. Otherwise, the code won't be identical.)
I believe you are wrong.

I'm sure I'm not. *The issue has been discussed before.


[snip]
But you compare two different types. I do know that for two
objects of the same type, if their adress compares equal, the
adresses must refer to the same object.

This is true for objects of different types as well, as long as
they are complete objects (e.g. not base classes or members of a
class or union). *Except, of course, objects with different
complete types must be different objects.
Can you tell me where in the standard, that requirement is stated? I
only found [expr.eq] which in the first paragraph states that:

"Pointers to objects or functions of the same type (after pointer
conversions) can be compared for equality. Two pointers of the same
type compare equal if and only if they are both null, both point to
the same function, or both represent the same address"

The way I read this paragraph, I do not see any support for your
statement, but you might have another paragraph/interpretation?
[snip]

/Peter
Jun 27 '08 #22
On Apr 29, 11:07 pm, peter koch <peter.koch.lar...@gmail.comwrote:
On 29 Apr., 10:06, James Kanze <james.ka...@gmail.comwrote:
On Apr 28, 11:12 pm, peter koch <peter.koch.lar...@gmail.comwrote:
On 28 Apr., 20:22, "Bo Persson" <b...@gmb.dkwrote:
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
>On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>>osama...@gmail.com wrote:
>[...]
>I certainly hope not. That wouldn't be conform. (I presume, of
>course, that the compiler will only do so if the objects are
>PODs as well. Otherwise, the code won't be identical.)
I believe you are wrong.
I'm sure I'm not. The issue has been discussed before.
[snip]
But you compare two different types. I do know that for two
objects of the same type, if their adress compares equal, the
adresses must refer to the same object.
This is true for objects of different types as well, as long as
they are complete objects (e.g. not base classes or members of a
class or union). Except, of course, objects with different
complete types must be different objects.
Can you tell me where in the standard, that requirement is
stated? I only found [expr.eq] which in the first paragraph
states that:
"Pointers to objects or functions of the same type (after
pointer conversions) can be compared for equality. Two
pointers of the same type compare equal if and only if they
are both null, both point to the same function, or both
represent the same address"
The way I read this paragraph, I do not see any support for
your statement, but you might have another
paragraph/interpretation?
And what does "after pointer conversions" mean?

In fact, the answer is that certain pointer conversions
(explicit or otherwise) do not change what object is being
pointed to (although the results likely lie about its type).
Thus, there is a long tradition of using "void (*)()" as a
"generic" pointer to function type: casting two pointers to
arbitrary functions to this type, then comparing them, should
tell you whether they point to the same function or not.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #23
On 30 Apr., 11:32, James Kanze <james.ka...@gmail.comwrote:
On Apr 29, 11:07 pm, peter koch <peter.koch.lar...@gmail.comwrote:


On 29 Apr., 10:06, James Kanze <james.ka...@gmail.comwrote:
On Apr 28, 11:12 pm, peter koch <peter.koch.lar...@gmail.comwrote:
On 28 Apr., 20:22, "Bo Persson" <b...@gmb.dkwrote:
peter koch wrote:
On 28 Apr., 10:40, James Kanze <james.ka...@gmail.comwrote:
On Apr 26, 9:38 am, "Bo Persson" <b...@gmb.dkwrote:
>osama...@gmail.com wrote:
[...]
I certainly hope not. That wouldn't be conform. (I presume, of
course, that the compiler will only do so if the objects are
PODs as well. Otherwise, the code won't be identical.)
I believe you are wrong.
I'm sure I'm not. *The issue has been discussed before.
[snip]
But you compare two different types. I do know that for two
objects of the same type, if their adress compares equal, the
adresses must refer to the same object.
This is true for objects of different types as well, as long as
they are complete objects (e.g. not base classes or members of a
class or union). *Except, of course, objects with different
complete types must be different objects.
Can you tell me where in the standard, that requirement is
stated? I only found [expr.eq] which in the first paragraph
states that:
"Pointers to objects or functions of the same type (after
pointer conversions) can be compared for equality. Two
pointers of the same type compare equal if and only if they
are both null, both point to the same function, or both
represent the same address"
The way I read this paragraph, I do not see any support for
your statement, but you might have another
paragraph/interpretation?

And what does "after pointer conversions" mean?
I see what you mean! I did not interpret that as widely as you: I took
pointer-conversions to mean e.g. removing constness of one of the
functions. Does the above also apply for objects?
As it is trivial to make two object-pointers compare equal when the
two objects are unrelated, I guess that can't be the case. IMHO if the
intension is as you say, the paragraphs above is quite badly
formulated.
>
In fact, the answer is that certain pointer conversions
(explicit or otherwise) do not change what object is being
pointed to (although the results likely lie about its type).
Thus, there is a long tradition of using "void (*)()" as a
"generic" pointer to function type: casting two pointers to
arbitrary functions to this type, then comparing them, should
tell you whether they point to the same function or not.
This is not a tradition, I've seen before.

/Peter
Jun 27 '08 #24

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Morgan | last post: by
9 posts views Thread by Just Me | last post: by
8 posts views Thread by Dennis Myrén | last post: by
7 posts views Thread by Wade Wegner | last post: by
22 posts views Thread by Arne | last post: by
11 posts views Thread by John Pass | last post: by
4 posts views Thread by John Sheppard | last post: by
reply views Thread by NPC403 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.