By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,301 Members | 1,766 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,301 IT Pros & Developers. It's quick & easy.

"delete this" question

P: n/a
Is the following code legal, moral, and advisable?

#include <iostream>

class A {
private:
int a;

public:
A() : a(42) {}
~A() {
std::cout << "A's destructor called, a is " << a << std::endl;
}
};

class B : public A {
private:
int b;

public:
B() : b(666) {}
~B() {
std::cout << "B's destructor called, b is " << b << std::endl;
}
void go() { delete this; }
};

int main() {
B *b=new B();
b->go();
return 0;
}

Specifically, are B and A's destructors still permitted to access
their class' member variables after the this pointer has been deleted?

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #1
Share this Question
Share on Google+
32 Replies


P: n/a
Christopher Benson-Manica wrote:
Is the following code legal, moral, and advisable?

#include <iostream>

class A {
private:
int a;

public:
A() : a(42) {}
~A() {
std::cout << "A's destructor called, a is " << a << std::endl;
}
};

class B : public A {
private:
int b;

public:
B() : b(666) {}
~B() {
std::cout << "B's destructor called, b is " << b << std::endl;
}
void go() { delete this; }
};

int main() {
B *b=new B();
b->go();
return 0;
}

Specifically, are B and A's destructors still permitted to access
their class' member variables after the this pointer has been deleted?


No, they aren't. But why would they need to? In your code, the destructors
are - just as usual - not called after the deletion, but as part of it.
And yes, you are allowed to delete an object in one of its member functions
as long as you make sure that nothing of it is accessed afterwards (i.e. in
your case, go() doesn't use any members of the object after the delete).

Whether 'delete this;' is a good design - well, I'd say that depends on
where and how the class is used.


Jul 23 '05 #2

P: n/a
* Christopher Benson-Manica:
Is the following code legal, moral, and advisable?
Yes, whatever, no.

#include <iostream>

class A {
private:
int a;

public:
A() : a(42) {}
~A() {
std::cout << "A's destructor called, a is " << a << std::endl;
}
};
Intended for polymorphism but non-virtual destructor: bad.

class B : public A {
private:
int b;

public:
B() : b(666) {}
~B() {
std::cout << "B's destructor called, b is " << b << std::endl;
}
void go() { delete this; }
};
Different lifetime management policies for A and B: bad.

B supports copying but requires dynamic allocation: bad.

I/o in non-i/o classes: bad.

int main() {
B *b=new B();
b->go();
return 0;
}

Specifically, are B and A's destructors still permitted to access
their class' member variables after the this pointer has been deleted?


No, but they don't.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #3

P: n/a
Christopher Benson-Manica wrote:
Is the following code legal, moral, and advisable? void B::go() { delete this; } B *b=new B();
b->go();
C++ defines the act of returning from a method after its object deletes.

Whether its moral and advisable is a team and project decision. Some
frameworks encapsulate all their owner pointers, so the caller rarely sees
'new' and never sees 'delete'. Within this pattern, objects often must
delete themselves, and they notify all the linking pointers to NULL
themselves.

Now let's make your code illegal:
#include <iostream>
class B;
class A {
private:
int a; B & m_b;
public: A(B &b) : a(42), m_b(b) {}
~A(); };

class B : public A { public:
int b;
public:
B() : b(666) {}
~B() {
std::cout << "B's destructor called, b is " << b << std::endl;
}
void go() { delete this; }
};
A::~A() {
std::cout << "A's destructor called, a is " << a << std::endl;
std::cout << "B has already destructed, so accessing b is illegal "
<< m_b.b << std::endl;
}
int main() {
B *b=new B();
b->go();
return 0;
}
During destruction, the derived class sub-object destructs first, so
accessing its members is undefined during the split second before the entire
object's storage releases. In practice, our 'b' will most likely return the
correct value.
Specifically, are B and A's destructors still permitted to access
their class' member variables after the this pointer has been deleted?


Their _own_ class's members, yes. They can also call methods. Destruction
happens just before releasing.

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #4

P: n/a
Rolf Magnus <ra******@t-online.de> wrote:
Whether 'delete this;' is a good design - well, I'd say that depends on
where and how the class is used.


<ot>Well, what I have in mind is a class that takes a function pointer
and puts it on a background thread; when the function is complete, the
class will take care of deleting itself and stopping the thread.
Sound reasonable?</ot>

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #5

P: n/a
Alf P. Steinbach <al***@start.no> wrote:
Yes, whatever, no.
Why do I feel like I'm conversing with an elf...?

<ot>I don't mind playing the role of Merry/Pippin - I really do look
like a hobbit :-)</ot>
Different lifetime management policies for A and B: bad.
So you're saying that if a given class never deletes itself, that no
subclass of that class should do so either...?
B supports copying but requires dynamic allocation: bad.
Is this a "Rule of Three" violation?
I/o in non-i/o classes: bad.


Explain? Please? :-)

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #6

P: n/a
Christopher Benson-Manica wrote:
Well, what I have in mind is a class that takes a function pointer
and puts it on a background thread; when the function is complete, the
class will take care of deleting itself and stopping the thread.
Sound reasonable?


Objects that wrap Win32 windows often respond to WM_CLOSE with 'delete
this'.

The distinction is these systems have an asynchronous event queue, so the
event triggering suicide comes from outside normal control flow.

But please don't thread if there are any alternatives!

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #7

P: n/a
Christopher Benson-Manica wrote:
Elf P. Steinbach wrote:

Different lifetime management policies for A and B: bad.


So you're saying that if a given class never deletes itself, that no
subclass of that class should do so either...?


I disagree. Let's apply the Liskov Substitution Principle.

If 'go()' were virtual and inherited, then users of A * p might call
p->go(), then expect p to remain valid. When p really points to a B, p->go()
would delete it. This would force the caller to become aware of which
subtype of A it used. This violates LSP, which states that child classes
should not surprise clients of parent classes who want to remain unaware
they are using the child class.

However, 'go()' is not virtual. Users of A cannot call 'go()', and users of
B cannot believe they have an A. So LSP does not apply, and there are no
surprises.

There are situations where all members of an inheritance graph should use
the same creation strategy. For example, OO designs often revolve around big
sessile objects that should never copy, and little value objects that copy
around freely. But I think this one 'go()' is the exception that proves the
rule.
B supports copying but requires dynamic allocation: bad.


Is this a "Rule of Three" violation?


Bleah. He's just nitpicking. You did not turn off B's copy constructor.

And B does not "require" dynamic allocation, because you could create B on
the stack and then decline to call go().
I/o in non-i/o classes: bad.


Explain? Please? :-)


Nitpicking again. You wrote 'cout' inside a class that should not surprise
clients with a side-effect that the user can see. Big friggin' deal.

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #8

P: n/a
Christopher Benson-Manica wrote:
Rolf Magnus <ra******@t-online.de> wrote:
Whether 'delete this;' is a good design - well, I'd say that depends on
where and how the class is used.


<ot>Well, what I have in mind is a class that takes a function pointer
and puts it on a background thread; when the function is complete, the
class will take care of deleting itself and stopping the thread.
Sound reasonable?</ot>


If the calling thread never needs any data of the called thread (maybe an
exit status or some calculated data stored in the thread object), yes.
Jul 23 '05 #9

P: n/a

"Christopher Benson-Manica" <at***@nospam.cyberspace.org> wrote in message
news:d9**********@chessie.cirr.com...
Is the following code legal, moral, and advisable?


Hmmm... what exactly would "immoral" C++ code be? :-)

(Sorry, couldn't resist.)

-H
Jul 23 '05 #10

P: n/a
Phlip <ph*******@yahoo.com> wrote:
Elf P. Steinbach wrote:

:-)
I disagree. Let's apply the Liskov Substitution Principle.
I feel better that I apparently inadvertently followed it, but I
appreciate knowing that it exists in any case.
Bleah. He's just nitpicking. You did not turn off B's copy constructor.
I don't mind nitpicks, if I understand the reasoning behind them.
And B does not "require" dynamic allocation, because you could create B on
the stack and then decline to call go().
B would require some documentation letting users know how it was
intended to be used, however. Maybe the way to do it (what I really
want to do) would be to wrap the class that destroys itself in a class
that doesn't care, making it impossible to misuse it?
Nitpicking again. You wrote 'cout' inside a class that should not surprise
clients with a side-effect that the user can see. Big friggin' deal.


Again, I don't mind nitpicks, especially since I know I'm living in a
bubble of sorts at my present place of employement...

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #11

P: n/a
Phlip <ph*******@yahoo.com> wrote:
But please don't thread if there are any alternatives!


Why do you say that? (non-rhetorical question)

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #12

P: n/a
Christopher Benson-Manica wrote:
Phlip wrote:
But please don't thread if there are any alternatives!


Why do you say that? (non-rhetorical question)


I research GUI architectures. One of the most common anti-patterns in this
space is "that button takes too long, and locks up the GUI while it runs, so
we made the button launch a thread".

Because I don't research OS kernels, embedded software, or other primitive
spaces that need threads to get anything done, I have never seen a situation
improved by a thread. Throwing a thread at a simple problem is worse than
'goto'. If a button starts a process that takes too long, the process itself
needs a better architecture.

Suppose I write a function that finds each file on your hard drive, and
inspects it for virii. The main work loop recurses, following the folders
like this:

click the button
open a folder
open a sub folder
read a file
is it infected?

That's going to take forever. While the button serves its click event, the
main GUI event queue cannot dispatch other messages, so the user perceives a
locked-up GUI.

Now let's upgrade this design, to decouple it and make it event driven:

click the button
change the button label to "cancel scan"
post a WM_TIMER message

on WM_TIMER message
fetch the next file name
if there's a next file
is it infected?
post a WM_TIMER message

The part "fetch the next file name" now uses a linked list of opened folders
to pull out the next one. This strategy decouples the act of traversing the
folder hierarchy from the act of inspecting for virii. Decoupling is good.
This strategy uses no threads, and uses a windows timer to cleanly start and
stop the scan. (Stop the scan by cancelling the current WM_TIMER message.)

The strategy is "event driven" because it doesn't store state in the current
location of control flow. The recursive function stores the current folder
state locally, on the stack, and coupled to the file scanner. The event
driven system stores the current folder state explicitely in a linked list.
This, in turn, makes the folder traversal system modular and reusable.

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #13

P: n/a
Phlip wrote:
Christopher Benson-Manica wrote:
Phlip wrote:
> But please don't thread if there are any alternatives!
Why do you say that? (non-rhetorical question)


I research GUI architectures. One of the most common anti-patterns in this
space is "that button takes too long, and locks up the GUI while it runs,
so we made the button launch a thread".

Because I don't research OS kernels, embedded software, or other primitive
spaces that need threads to get anything done, I have never seen a
situation improved by a thread. Throwing a thread at a simple problem is
worse than 'goto'. If a button starts a process that takes too long, the
process itself needs a better architecture.


I agree with Philip. Threads introduce a another level of complexity, thus
overstraining most developers. You have to change your way of thinking
before you can get a real gain by using threads.
Suppose I write a function that finds each file on your hard drive, and
inspects it for virii. The main work loop recurses, following the folders
like this:

click the button
open a folder
open a sub folder
read a file
is it infected?

That's going to take forever. While the button serves its click event, the
main GUI event queue cannot dispatch other messages, so the user perceives
a locked-up GUI.

Now let's upgrade this design, to decouple it and make it event driven:

click the button
change the button label to "cancel scan"
post a WM_TIMER message

on WM_TIMER message
fetch the next file name
if there's a next file
is it infected?
post a WM_TIMER message

The part "fetch the next file name" now uses a linked list of opened
folders to pull out the next one. This strategy decouples the act of
traversing the folder hierarchy from the act of inspecting for virii.
Decoupling is good. This strategy uses no threads, and uses a windows
timer to cleanly start and stop the scan. (Stop the scan by cancelling the
current WM_TIMER message.)

The strategy is "event driven" because it doesn't store state in the
current location of control flow. The recursive function stores the
current folder state locally, on the stack, and coupled to the file
scanner. The event driven system stores the current folder state
explicitely in a linked list. This, in turn, makes the folder traversal
system modular and reusable.


Of course its necessary to obtain the linked list of files (in the same
manner). And we silently assume, that "is it infected" will never block
(block from the point of view of the user). If we start another thread or
process to do the scanning, we would be able to kill it immediately
whenever the frustrated user hits the cancel button.
Every solution has its drawbacks (and sometimes some advantages).

Mathias

PS: sorry for being OT.

Jul 23 '05 #14

P: n/a
Phlip <ph*******@yahoo.com> wrote:

(Moving to comp.programming.threads - if you don't read that group,
come back to clc++...)
Because I don't research OS kernels, embedded software, or other primitive
spaces that need threads to get anything done, I have never seen a situation
improved by a thread. Throwing a thread at a simple problem is worse than
'goto'. If a button starts a process that takes too long, the process itself
needs a better architecture.


Well, my specific intention for this thread is for it to take care of
caching the contents of a file in memory as my program is starting.
The main thread can't wait that long, because the program is a Windows
service and the service manager isn't willing to wait forever for my
program to start. I know I could do this other ways, but is there
really a big disadvantage to spawning a thread to handle the caching
of this file?

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #15

P: n/a
Howard wrote:
Hmmm... what exactly would "immoral" C++ code be? :-)


Dive in: http://sf.net

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #16

P: n/a
Christopher Benson-Manica wrote:
B would require some documentation letting users know how it was
intended to be used, however. Maybe the way to do it (what I really
want to do) would be to wrap the class that destroys itself in a class
that doesn't care, making it impossible to misuse it?


Ding! Objects should be easy to use right and hard to use wrong.

About "documentation", try some test cases that show each object's normal
lifecycle. Class users (including yourself) will be unlikely to deviate.
Nitpicking again. You wrote 'cout' inside a class that should not surprise clients with a side-effect that the user can see. Big friggin' deal.


Again, I don't mind nitpicks, especially since I know I'm living in a
bubble of sorts at my present place of employement...


How DARE you write 'cout' inside a non-IO class?!!

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #17

P: n/a
Mathias Waack wrote:
Of course its necessary to obtain the linked list of files (in the same
manner). And we silently assume, that "is it infected" will never block
(block from the point of view of the user). If we start another thread or
process to do the scanning, we would be able to kill it immediately
whenever the frustrated user hits the cancel button.
Every solution has its drawbacks (and sometimes some advantages).
"Is it infected", in turn, runs a list of potential virii, so we have
another spot to decouple.

From here, event-driven architectures are overwhelmingly easy to switch to
use threads, because the places between the events, for other activities,
are also good places for semaphores. But this doesn't work the opposite way.
Poorly architected systems might be hard to convert from threads to events.
So, either way, focus on good architecture.

Christopher Benson-Manica wrote:
Well, my specific intention for this thread is for it to take care of
caching the contents of a file in memory as my program is starting.
The main thread can't wait that long, because the program is a Windows
service and the service manager isn't willing to wait forever for my
program to start. I know I could do this other ways, but is there
really a big disadvantage to spawning a thread to handle the caching
of this file?


Short term, no. Removing this thread won't force the architecture to
improve.

Long term, use ReadFileEx() in asynchronous mode. If you need notification
that the file arrived, you can multiplex the file event semaphore in with
the window messages using MsgWaitForMultipleObjects().

Or start a window timer that wakes up and reads a small chunk of the file.
The file system will buffer the real file asynchronously, behind these
reads, meaning they shouldn't block the GUI.

Notice the tutorials tend to teach threading _before_ they teach these
high-powered methods to cleanly avoid threads...

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #18

P: n/a
Phlip <ph*******@yahoo.com> wrote:
Long term, use ReadFileEx() in asynchronous mode. If you need notification
that the file arrived, you can multiplex the file event semaphore in with
the window messages using MsgWaitForMultipleObjects().
Well, I guess I should have chosen my words more carefully - the file
has the contents of a list of data structures, each of which needs to
be parsed before the program can begin its real work.
Notice the tutorials tend to teach threading _before_ they teach these
high-powered methods to cleanly avoid threads...


Yes, I do notice that, and I'll remember them (hopefully) next time I
do GUI work :-)

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #19

P: n/a
Phlip <ph*******@yahoo.com> wrote:
How DARE you write 'cout' inside a non-IO class?!!


Well, neither I nor my coworkers much care, but presumably there are
places and bosses where that is the standard, so it's worth at least
being thus forewarned...

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #20

P: n/a
Christopher Benson-Manica wrote:
How DARE you write 'cout' inside a non-IO class?!!


Well, neither I nor my coworkers much care, but presumably there are
places and bosses where that is the standard, so it's worth at least
being thus forewarned...


Ookay. Read /Design Patterns/. It sounds like your colleagues are not even
at this level yet...
Long term, use ReadFileEx() in asynchronous mode. If you need notification that the file arrived, you can multiplex the file event semaphore in with the window messages using MsgWaitForMultipleObjects().


Well, I guess I should have chosen my words more carefully - the file
has the contents of a list of data structures, each of which needs to
be parsed before the program can begin its real work.


Are you trying to read blocks in one thread and parse in another? If so,
there's no point. The hard drive controller reads file tracks while the disk
is spinning, so their data blocks are ready each time you call ReadFile().
So you already have blocks reading in one thread and parsing in another.

We are nearing the concept "Premature optimization" here...

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #21

P: n/a
Phlip <ph*******@yahoo.com> wrote:
Ookay. Read /Design Patterns/. It sounds like your colleagues are not even
at this level yet...
Well, on the bright side, it's sitting within easy reach on this desk.
Unfortunately there's very little designing or redesigning going on
relative to the amount of maintaining...
Are you trying to read blocks in one thread and parse in another? If so,
there's no point. The hard drive controller reads file tracks while the disk
is spinning, so their data blocks are ready each time you call ReadFile().
So you already have blocks reading in one thread and parsing in another.


No, I'm reading and parsing in one thread while the rest of the
initialization continues courtesy of the service control manager.

--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
Jul 23 '05 #22

P: n/a
* Phlip:
* Christopher Benson-Manica:
* Alf P. Steinbach wrote:
Different lifetime management policies for A and B: bad.


So you're saying that if a given class never deletes itself, that no
subclass of that class should do so either...?


I disagree.

...
there are no urprises.


Sorry, the analysis is flawed.

A B-object may be terminated unexpectedly when it is used in place of
an A, under A assumptions.

For example, the code holding a presumed A (really a B) may be unaware
that some other, event-driven code holds a pointer to the B object as
a B, and terminates it...

Without analysing your analysis in detail I think the flaw is perhaps an
assumption that _all_ code must be either "B-aware" or not.

That's not the case.

B supports copying but requires dynamic allocation: bad.


Is this a "Rule of Three" violation?


Bleah. He's just nitpicking. You did not turn off B's copy constructor.


It's an important nitpick.

If a B-object is inadvertently copied, the result is undefined.

That means a possible later catastrophic error that nobody understand
at all.
And B does not "require" dynamic allocation, because you could create B on
the stack and then decline to call go().


Well, the Titanic was a safe ship, because you could refrain from bording.

I/o in non-i/o classes: bad.


Explain? Please? :-)


Nitpicking again. You wrote 'cout' inside a class that should not surprise
clients with a side-effect that the user can see. Big friggin' deal.


Affects reusability and not the least, complicates the code (in general).

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #23

P: n/a
Phlip wrote:

Now let's upgrade this design, to decouple it and make it event driven:

click the button
change the button label to "cancel scan"
post a WM_TIMER message

on WM_TIMER message
fetch the next file name
if there's a next file
is it infected?
post a WM_TIMER message

The part "fetch the next file name" now uses a linked list of opened folders
to pull out the next one. This strategy decouples the act of traversing the
folder hierarchy from the act of inspecting for virii. Decoupling is good.
This strategy uses no threads, and uses a windows timer to cleanly start and
stop the scan. (Stop the scan by cancelling the current WM_TIMER message.)

The strategy is "event driven" because it doesn't store state in the current
location of control flow. The recursive function stores the current folder
state locally, on the stack, and coupled to the file scanner. The event
driven system stores the current folder state explicitely in a linked list.
This, in turn, makes the folder traversal system modular and reusable.


How would you handle multiple different asynchronous actions with WM_TIMER
messages which may or may not be invoked concurrently? You'd have to multiplex
them somehow and guarantee fairness in execution.

--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.
Jul 23 '05 #24

P: n/a
Christopher Benson-Manica wrote:
Is the following code legal, moral, and advisable?

#include <iostream>

class A {
private:
int a;

public:
A() : a(42) {}
~A() {
std::cout << "A's destructor called, a is " << a << std::endl;
}
};

class B : public A {
private:
int b;

public:
B() : b(666) {}
~B() {
std::cout << "B's destructor called, b is " << b << std::endl;
}
void go() { delete this; }
};

int main() {
B *b=new B();
b->go();
return 0;
}


Yes, but you must be *very* careful. See the FAQ for details.

Kristo

Jul 23 '05 #25

P: n/a
Christopher Benson-Manica wrote:
Phlip <ph*******@yahoo.com> wrote:

(Moving to comp.programming.threads - if you don't read that group,
come back to clc++...)
Because I don't research OS kernels, embedded software, or other primitive
spaces that need threads to get anything done, I have never seen a situation
improved by a thread. Throwing a thread at a simple problem is worse than
'goto'. If a button starts a process that takes too long, the process itself
needs a better architecture.


Well, my specific intention for this thread is for it to take care of
caching the contents of a file in memory as my program is starting.
The main thread can't wait that long, because the program is a Windows
service and the service manager isn't willing to wait forever for my
program to start. I know I could do this other ways, but is there
really a big disadvantage to spawning a thread to handle the caching
of this file?


No, not "a big disadvantage".

Threads used correctly (it takes some practice) can enable
quite complex & responsive applications. The "threads good
or bad" discussion depends on many complex factors; like all
tools, they have their uses, but they (nor anything else) are
not a "silver bullet".

Larry
Jul 23 '05 #26

P: n/a
Joe Seigh wrote:
How would you handle multiple different asynchronous actions with WM_TIMER
messages which may or may not be invoked concurrently? You'd have to multiplex them somehow and guarantee fairness in execution.


Besides WM_TIMER comes with a payload integer to distinguish message
streams?

At some point you give up and add a thread. You use it as carefully and
judiciously as possible. Don't use threads like a baby who has just learned
to use a hammer, and thinks everything looks like a nail.

(Note I'm busting on languages that make threads "easy"...)

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #27

P: n/a
Phlip wrote:
Joe Seigh wrote:

How would you handle multiple different asynchronous actions with WM_TIMER
messages which may or may not be invoked concurrently? You'd have to


multiplex
them somehow and guarantee fairness in execution.

Besides WM_TIMER comes with a payload integer to distinguish message
streams?

At some point you give up and add a thread. You use it as carefully and
judiciously as possible. Don't use threads like a baby who has just learned
to use a hammer, and thinks everything looks like a nail.

(Note I'm busting on languages that make threads "easy"...)


Well, it's a nice trick I suppose if you don't have threads. Otherwise it seems
a bit contrived and makes things more complicated than they need to be. One of
the reasons for threading is modularization as much as for asynchronicity or
concurrency. It's to make things simpler. I think the main culprit is not so well
thought out OO design and design patterns which cause more problems than they
solve. The "anti-patterns" if you will. I haven't messed with GUI programming
lately but the early MVC implementations in Java were atrocious and a major pain
to work with. Probably if you had the GUI primatives designed by people who
understood threading a little better and knew the thread design patterns useful
for keeping out of trouble, not so many people would find multi-threaded GUI
programming quite so traumatic. And c.p.t. wouldn't have so many posts requesting
information on deadlock detection (invariably by people doing OO GUI programming).

--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.
Jul 23 '05 #28

P: n/a
Christopher Benson-Manica wrote:
Well, my specific intention for this thread is for it to take care of
caching the contents of a file in memory as my program is starting.
The main thread can't wait that long, because the program is a Windows
service and the service manager isn't willing to wait forever for my
program to start. I know I could do this other ways, but is there
really a big disadvantage to spawning a thread to handle the caching
of this file?
Flirting with Deceased Equine Flaggelation Mode, but...

In your case you are a service, not a simple app, so your program already
solves concurrency issues, so one more shouldn't hurt.

But note that Win32 services uses the GetMessage() DispatchMessage() event
loop, so my multiplexing solution would still work.

Another principle here is to push as much work, creativity, and risk down to
the OS as possible. ReadFileEx().

Joe Seigh wrote:
Well, it's a nice trick I suppose if you don't have threads.
Per the other posts: An event driven architecture is good whether or not you
thread, and threading should not be used to avoid supplying the good
architecture.
Otherwise it seems
a bit contrived and makes things more complicated than they need to be. One of the reasons for threading is modularization as much as for asynchronicity or concurrency. It's to make things simpler.
And threads can break encapsulation, and force an object in one thread to
become aware of the private details and activities of an object in another
thread, waiting for it to release a semaphore.

(Before the denizens of cpt think I'm some kind of anti-threading zealot
newbie, I grew up developing for AmigaOS, which works like a modern embedded
system. Threads are necessary, and one false move with threads would send
the CPU into FIREWORKS_DISPLAY mode.)
I think the main culprit is not so well
thought out OO design and design patterns which cause more problems than they solve. The "anti-patterns" if you will. I haven't messed with GUI programming lately but the early MVC implementations in Java were atrocious and a major pain to work with. Probably if you had the GUI primatives designed by people who understood threading a little better and knew the thread design patterns useful for keeping out of trouble, not so many people would find multi-threaded GUI programming quite so traumatic.
Are you implying that there are GUI architectures that thread each input
event as the _default_??
And c.p.t. wouldn't have so many posts requesting
information on deadlock detection (invariably by people doing OO
GUI programming).


I rest my case.

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #29

P: n/a
"Phlip" <ph*******@yahoo.com> schrieb:
click the button
open a folder
open a sub folder
read a file
is it infected?

click the button
change the button label to "cancel scan"
post a WM_TIMER message

on WM_TIMER message
fetch the next file name
if there's a next file
is it infected?
post a WM_TIMER message

The part "fetch the next file name" now uses a linked list of opened
folders to pull out the next one. This strategy decouples the act of
traversing the folder hierarchy from the act of inspecting for
virii. Decoupling is good. This strategy uses no threads, and uses a
windows timer to cleanly start and stop the scan. (Stop the scan by
cancelling the current WM_TIMER message.)


No, I cannot agree with this. This is not a real decoupling. The
former thread is indeed decoupled from the GUI and perhaps the GUI can
really remain active but there's still a new coupling on another
controlling instance (in this case a timer). In my opinion this timer
destroys the whole algorithm by cutting it into infinitesimal small
pieces. What if I want to execute it at once? I hate destroyed
algorithms especially if they are complicated. Good luck for debugging
this!

Some years ago I implemented an editor control supporting syntax
highlighting. After some weeks of development I found it a good idea
to use a background thread for the parsing and coloring algorithm
itself because opening a large file could take a really long time.
During this time my control did display the text in black and white,
some seconds later the color came along. Every pressed key did start
or reset a timer and after a short while the thread was executed again
to show the text in perhaps new colors. Still today I think this was a
good reason to use a thread in a GUI.

T.M.
Jul 23 '05 #30

P: n/a
Torsten Mueller wrote:
No, I cannot agree with this. This is not a real decoupling. The
former thread is indeed decoupled from the GUI and perhaps the GUI can
really remain active but there's still a new coupling on another
controlling instance (in this case a timer). In my opinion this timer
destroys the whole algorithm by cutting it into infinitesimal small
pieces. What if I want to execute it at once? I hate destroyed
algorithms especially if they are complicated.
A major metric for decoupling is testability. The "before" pattern cannot
test the folder recursion system isolated from the file scanner. The "after"
pattern can test the WM_TIMER, the file scanner, and the folder recursion,
all three isolated from each other.
Good luck for debugging this!
Uh, I actually implemented the "after" pattern, using Test-Driven
Development, and when I was finished adding tests it had no bugs. I often
used the debugger to step thru the code and watch it work, but I never
_needed_ to use the debugger.

If changing requirements caused a bug, in the future, and if tests did not
catch it, the test cases would make an excellent platform for debugging (or
even just for trace statements) to detect the problem. Then new tests on the
problem, and the existing tests, would prevent the bug fix from creating new
bugs.

On review, the client liked the code structure and my coding style.

So, no luck will ever be required to debug my implementation of this
pattern.

Threads make bug repression a nightmare because race conditions might behave
in simple tests different from in production.
Some years ago I implemented an editor control supporting syntax
highlighting. After some weeks of development I found it a good idea
to use a background thread for the parsing and coloring algorithm
itself because opening a large file could take a really long time.
If you need to eat a sandwich in one hand and drive with the other, thread.
During this time my control did display the text in black and white,
some seconds later the color came along. Every pressed key did start
or reset a timer and after a short while the thread was executed again
to show the text in perhaps new colors. Still today I think this was a
good reason to use a thread in a GUI.


How does VS6 or VS7 highlight its syntax in realtime, without a refresh
period?

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand
Jul 23 '05 #31

P: n/a
"Phlip" <ph*******@yahoo.com> schrieb:
I often used the debugger to step thru the code and watch it work,
but I never _needed_ to use the debugger.
You never had to explain an application somebody else wrote, perhaps a
person using a strange programming style (this is because normally he
was a lisp programmer), speaking another native language and nobody
has seen him for years?
If changing requirements caused a bug, in the future, and if tests
did not catch it, the test cases would make an excellent platform
for debugging (or even just for trace statements) to detect the
problem. Then new tests on the problem, and the existing tests,
would prevent the bug fix from creating new bugs.
No. You cannot guarantee a software quality by tests. I always have to
fight against this opinion (especially managers do think like this).
Tests are nothing but an emergency break, something one can use too if
nothing else has left (especially if the software's design and
implementation do not guarantee it's quality).
On review, the client liked the code structure and my coding style.
And who is the client? A customer? I mean - a user?
Threads make bug repression a nightmare because race conditions
might behave in simple tests different from in production.
Normally threads are testable non-threaded! In most cases you can
write down an entire algorithm, debug it, test it, optimize it, and
then you can say, this function runs as a thread now. OK, if you have
several concurrent threads it would be more complicated.
How does VS6 or VS7 highlight its syntax in realtime, without a
refresh period?


These applications do support just a few hard coded languages. My
editor control was open to support *any* language having been defined
by semantic rules in an initialization file. And I supported nested
comments (this takes a lot of time more than just C/C++ comments). If
you take a look upon Visual Assist (coloring function names, class
names, matching brackets ... in Visual Studio) you will see threading
is surely needed for background parsing and coloring of a text.

T.M.
Jul 23 '05 #32

P: n/a
Torsten Mueller wrote:
Phlip schrieb:
I often used the debugger to step thru the code and watch it work,
but I never _needed_ to use the debugger.
You never had to explain an application somebody else wrote, perhaps a
person using a strange programming style (this is because normally he
was a lisp programmer), speaking another native language and nobody
has seen him for years?


That's out of context. I described a project I wrote from scratch.

If someone gives you a program built by Debugger-Driven Development, you
have no choice but to pick up where they left off.
No. You cannot guarantee a software quality by tests.
Of course not. But you sure as hell can avoid long bug hunts.
I always have to
fight against this opinion (especially managers do think like this).
Tests are nothing but an emergency break, something one can use too if
nothing else has left (especially if the software's design and
implementation do not guarantee it's quality).


So why not write tests before writing the tested code? That makes them
efficient to write, turning them from a backup situation into a way to
propel development.
On review, the client liked the code structure and my coding style.


And who is the client? A customer? I mean - a user?


A programmer who must maintain that code.
Threads make bug repression a nightmare because race conditions
might behave in simple tests different from in production.


Normally threads are testable non-threaded! In most cases you can
write down an entire algorithm, debug it, test it, optimize it, and
then you can say, this function runs as a thread now. OK, if you have
several concurrent threads it would be more complicated.


Things which are hard to test should be avoided. That's a sign they bring
too much complexity.

If they are, in fact, the simplest spot between even more complexity, tests
can detect this situation, and can reinforce the fixes.

--
Phlip
http://www.c2.com/cgi/wiki?ZeekLand

Jul 23 '05 #33

This discussion thread is closed

Replies have been disabled for this discussion.