|
Basically i wanted the parent class to be totally unaware of which child object was been used to achieve my task.
Seemed achieveable apart usingpure vritual interfaces apart from one parameter, it's a pointer to a 2d array in one of the object and a pointer to a string in another.
Guess my two option are:
* Use a void* and cast appropiately in each of the objects.
* Use an intermedia object which combines the two child objects.
Any other ideas? Think i am going to have to get my old uni notes out!
Thanks Again
Rich
|
|
|
|
|
* Use a void* and cast appropiately in each of the objects.
This is a no-no in OOP.
* Use an intermedia object which combines the two child objects.
Maybe, but I don't have a clear idea of your general scenario to be able to ellaborate mor on this approach. The basic question here is (IMHO): How does the parent object obtain the argument to its (of unknown type) child object? Depending on the various agents involved in the argument-passing thing, one of several design patterns can be applied.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Is there a way to enumerate through the list of available comm ports in the same way as you can enumerate through the list of printers?
Systems AXIS Ltd - Software for Business ...
|
|
|
|
|
Look for PJ Plaugher's library on this site. There is a function in there that will do this. There is a link to it in the left-hand column.
|
|
|
|
|
|
I'm currently having problems with inserting/displaying large amounts of items in a treectrl under XP. It seems that there is a limit on the amount of manageable subitems.
When adding more than 65.000 elements under one node (yes, i know thats too much to be useful for the user), the tree starts to behave strange: items disapear, nodes disapear, focus disapears, the scrollbar is not in sync with the content, etc.
All this under XP build 2600.
If you want to try, just create a simple dialog based application with a treectrl and add the following code into the OnInitDialog() function:
<br />
HWND hTree = ...your handle here;<br />
TCHAR buf1[64];<br />
TCHAR buf2[64];<br />
<br />
TVINSERTSTRUCT is;<br />
is.hParent = TVI_ROOT;<br />
is.hInsertAfter = TVI_ROOT;<br />
is.itemex.mask = TVIF_TEXT;<br />
is.itemex.pszText = _T("Root");<br />
<br />
HTREEITEM hItem = TreeView_InsertItem(hTree, &is);<br />
<br />
is.hParent = hItem;<br />
is.hInsertAfter = TVI_LAST;<br />
<br />
for(int i = 0; i < 5; ++i)<br />
{<br />
sprintf(buf1, _T("SubItem %05i"), i);<br />
is.itemex.pszText = &buf1[0];<br />
<br />
HTREEITEM hSubItem = TreeView_InsertItem(hTree, &is);<br />
TVINSERTSTRUCT is2 = is;<br />
<br />
is.hParent = hSubItem;<br />
is.itemex.pszText = &buf2[0];<br />
for(int n = 0; n < 1024*65; ++n)<br />
{<br />
sprintf(buf2, _T("Item %03i/%06i"), i, n);<br />
TreeView_InsertItem(hTree, &is2);<br />
}<br />
}<br />
And there is no difference between using CTreeCtrl::InsertItem or the SDK macro.
cheers
Andreas
|
|
|
|
|
Here's an interesting problem (I think)...
Our software uses a bunch (tens of thousands) of "slice" objects - each about 10k. Up until this morning, we were allocating them when we load the data file, and deallocating in the DeleteContents(). This causes a serious performance hit - it has been found that the allocation takes over 60% of the load time.
So I figured I'd just throw in a simple cache. I'd allocate a ton of slices up front, and just dish them out when I needed them. Did that, and now slice acquistion at load time is negligable.
The trouble is, I allocate slices in 10000 object chunks - that is, when the cache is exhausted, I run a for-loop 10000 times allocating a new slice each time. For some of our data files, this happens 4 or 5 times each load. I know this is inefficient, so I added another structure in the cache. When I need a new block, I allocate a 10000 * sizeof(slice) chunk, and handle the internal pointers myself.
No problem, right? In theory, this should speed up my load and access and whatnot a whole bunch... if it would just stop crashing.
Turns out, sizeof(object) just gives the size of the data members inside said object. My slice implements a custom interface full of pure virtual methods. sizeof() doesn't take into consideration the vtable and whatnot at the start of the class...
So my question is this: is there some way to determine (programmatically or through the compiler) exactly how large my object is so I can allocate a chunk of blank memory and manage my objects myself? Or should I just revert to the for loop?
Thanks.
J
|
|
|
|
|
sizeof(object) returns the size of the entire object, vtable pointers and all, so your problem should be elsewhere.
If you allow me to do a little of self-publicity, maybe you can give it a try to a block allocator for STL containers I wrote and posted here at CodeProject.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
I appreciate the correction regarding sizeof() - it means I'm on the right track. I had a look at your allocator, and that's exactly what I'm trying to do, more or less. I think I'd like to try to correct my code before trying your allocator - no offence intended, I like to understand the problem and the solution before I cut and paste.
Having read through your article and your code, I have a feeling my problem is just a casting issue. Here is a small chunk of the code (error checking removed):
class ISlice {
virtual void Something() = 0;
};
class CDataSlice : public ISlice {
...
};
// blocks
std::list<byte *> CDataSliceCache::m_listBlocks;
// free/unused slices
std::list<CDataSlice *> CDataSliceCache::m_listFree;
void CDataSliceCache::AddBlock() {
byte *pBase;
CDataSlice *pSlice;
// allocate and store the block
pBase = new byte[m_nBlockSize * sizeof(CDataSlice)];
m_listBlocks.push_back(pBase);
// add internal pointers to free slice list
for(int i = 0; i < m_nBlockSize; i++) {
pSlice = reinterpret_cast<CDataSlice *>(&pBase[i * sizeof(CDataSlice)]);
m_listFree.push_back(pSlice);
}
}
Does that reinterpret_cast<> look like it will work? If so, maybe my trouble is elsewhere...
Thanks for the input.
J
|
|
|
|
|
Looks like the reinterpret_cast<> is not the problem.
How do you manage construction and destruction of a CDataSlice upon a block of memory (say pBock )? For construction you should be using new ((void *)pBlock) CDataSlice(...) , whereas for destruction the trick is calling pSlice->CDataSlice::~CDataSlice() . If you don't do it this way, you might be freeing the same block of memory twice.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Hmmm. Guess I'm stupid. I'm not "constructing" the slice at all. I just assumed (incorrectly) that allocating the memory and setting a pointer to the front was enough.
Of course, I now realize that the vtable never gets setup, especially since I memset the allocated block to 0s. That's my crash problem.
The solution is to "construct" the objects in the block of memory, but I'm not sure I know how to do that, and I can't see how the new ((void *) pBlock) CDataSlice() would do it.
Would I use it ignoring the return?
for(i = 0; i < m_nBlockSize; i++) {
new ((void *) &pBlock[i * sizeof(CDataSlice)]) CDataSlice;
}
Guess not. Doesn't compile. I need some way of telling the compiler to construct the CDataSlice in the pre-allocated memory.
Any clues as to how to do that?
J
|
|
|
|
|
Jamie Hale wrote:
Guess not. Doesn't compile. I need some way of telling the compiler to construct the CDataSlice in the pre-allocated memory.
Any clues as to how to do that?
Yep - use a placement new. Look at the CSimpleArray class for an example.
|
|
|
|
|
Ok, CSimpleArray is a damn hard class to find. MSDN mentions it, and uses it in several examples, but fails to define it. Had to find an article by Grimes which makes me think it's probably all over my ATL books...
Anyways, I found that it uses an operator new overload with the second argument. I've also found a few other examples of it's use - the placement one. Unfortunately, I can't for the life of me get it to work in my code.
You can follow along in the thread below if you haven't got bored yet.
J
|
|
|
|
|
Guess not. Doesn't compile.
It should compile . Try putting extra parentheses everywhere:
new ((void *) (&pBlock[i * sizeof(CDataSlice)])) CDataSlice();
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
It does not compile. It's complaining about an ( and I've been over it a dozen times. It's almost as if it doesn't like this whole placement type allocation...
But if I understand correctly, new (something) class; translates into class::operator new(size, something); doesn't it? I haven't defined that override in the class, but a few other articles I've read seem to imply that a simple implementation of it gets built for you.
inline void * operator new(size_t, void *p) {
return p;
}
Mind you, this gives me odd compile errors when I add it...
I'm at a loss. I can appreciate what you're suggesting, and it looks like that's exactly what I need, but the damn thing don't work.
Any other suggestions? Any links to reasonable documentation on the placement-new would help too! MSDN is seriously lacking here.
Thanks so much for your time.
J
|
|
|
|
|
Excuse my stubbornness, but it's got to work. Maybe the compiler is getting confused... Would you mind trying this piece of code instead?
void * pv=(void *)&pBlock[i * sizeof(CDataSlice)];
CDataSlice * ps=new (pv) CDataSlice();
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Not your stubbornness... my compiler's (VC6.0 with the latest SP).
I copied that code verbatim, and it yaks on the second line saying "syntax error : identifier 'pv'"
It's almost like my compiler isn't set up to understand that?
J
|
|
|
|
|
Hmmm, just found that you're supposed to #include <new>, but adding it didn't change anything. It must get included through one of the other STL headers?
J
|
|
|
|
|
I think I got it. Does this article ring a bell for you? Seems MFC can interfere with placement new.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
That's it exactly. I can't imagine how long it took you to track that down, but thanks a ton.
I should have seen that. At one point in time, I was getting error C2061: syntax error : identifier 'THIS_FILE' where I defined my operator new. And that's because of MFC's debug memory manager. As soon as I commented the little boilerplate #ifdef blurb, it works fine.
Thanks so much for your time. I sure hope the block-allocation strategy actually helps performance!! If not, at least I learned about the whole placement-new thingy.
J
|
|
|
|
|
'scuse me butting in again, but I think you _do_ need the override.
Here's a quick link from google:
http://www.glenmccl.com/tip_025.htm
hmmm...
Actually, bad example - doesn't explain the need for explicit calls to the dtor for cleanup.
Got a copy of Stroustrup handy? That will explain it. Look for placement in the index - should show up under a Special Member Funtions section or some such...
|
|
|
|
|
No worries - I appreciate ALL input. I'm really stumped here.
I added the override, but it doesn't seem to help.
inline void *operator new(size_t, void *p) {return p;}
I don't want to do anything with it. The memory is already allocated - I just need the vtable setup for me and the default constructor called.
J
|
|
|
|
|
Almost there... I haven't quite got it conceptually (which makes it worth hacking on, I guess) but this _seems_ to work.
Just some details of the delete declaration to argue with the compiler about... there are some special rules for delete at the class level. BTW the new could be global as well...
Not sure if the vtable pointer is included in the size - tracking p in the new op shows 16?
Facinating stuff...
#include "windows.h"
#include <iostream.h>
#include <stdio.h>
class foo {
public:
foo(){}
~foo(){
cout << "destroying " << i << endl;
}
foo(int n) : i(n){
cout << "constructing " << i << endl;
};
void* operator new (size_t sz, void* p) {
return p;
}
<code>
private:
int i, j, k, l;
};
int main(int argc, char* argv[])
{
HANDLE hHeap = GetProcessHeap();
if(hHeap) {
void * vp = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 20 * sizeof(foo));
foo * fooPs[20];
for (int i = 0; i < 20; ++i) {
fooPs[i] = new ((foo*)vp+i)foo(i);
}
for ( i = 0; i < 20; ++i ) {
fooPs[i]->foo::~foo();
}
HeapFree(hHeap, 0, vp);
}
int x;
cin >> x;
return 0;
}
|
|
|
|
|
Yeah, I built a tiny little test program and it seemed to work fine. I'm convinced that placement-new exists and works.
There would be no vtable in the class you gave. If you had derived your class from another with virtual members, it would have been 4 bytes bigger.
class V {
public:
V() {}
~V() {}
virtual void wank() = 0;
};
class B {
public:
B() {}
~B() {}
void wank() {}
};
class D1 : public V {
int i,j,k,l;
public:
D1() {}
~D1() {}
void wank() {}
};
class D2 : public B {
int i,j,k,l;
public:
D2() {}
~D2() {}
void wank() {}
};
ASSERT(sizeof(D1) == 20);
ASSERT(sizeof(D2) == 16);
Anyways, Joaquín has hit on it. MFC's DEBUG memory management interferes. I'll post the details as a reply to his post above...
Thanks for your input.
J
|
|
|
|
|
Cool! Here's another take on it:
http://www.michaelmoser.org/memory.htm
might make the class more 'portable'.
A search on Google for 'placement new MFC' turns up lots of stuff. Looks like the relations between crtdbg, MFCs allocation routines, heaps etc. would make for a good article.
I'd give it a try, but I seem to have deleted everything I new...
|
|
|
|