Be Newsletter
Issue 80, July 2, 1997
Table of Contents
BE EVENTS: BeOS demo in Research Triangle Park in N. Carolina
- Tuesday July 8, 1997: Research Triangle Park, North
Carolina
BeOS Demo at the Triangle Macintosh Users Group General
Meeting
When: Tuesday July 8 at 7:00pm
Where:
U.S. EPA (Environmental Protection Agency)
Environmental Research Center Auditorium
Highway 54 & Alexander Drive
Research Triangle Park, North Carolina
For directions, please see:
http://www.tmug.org/meetinglocation.html
BE ENGINEERING INSIGHTS: Using Function Objects In The Be Messaging Model
By Pavel Cisler
I would like to share with you a neat technique of using
function objects with the BMessage class to simplify some
common patterns in your application code.
Consider a menu containing an item for every window open in
an application. When an item from the menu is selected, we
would like to call some code to make the corresponding
window active. Part of the menu building code might look
something like this:
for (int32 index = 0; index < windowCount; index++) {
BWindow *window = windowList->ItemAt(index);
BMessage *message = new BMessage(kSelectWindowMessage);
message->AddInt32("index", index);
menu->AddItem(new BMenuItem(window->Title(), message));
}
We would then have a corresponding MessageReceived function,
that would contain the following code:
switch (message->what) {
case kSelectWindowMessage:
int32 index;
message->FindInt32("index", &index);
windowList->ActivateIndexedWindow(index);
...
where windowList is of an unspecified WindowList class.
When we were building up the window menu, we knew ahead of
time that we would end up calling ActivateIndexedWindow when
an item was selected, and passing it the index as a
parameter, we knew we would call it on the windowList
object. It seems like it would be convenient to be able to
hide the corresponding message dispatching and parameter
packaging code into some more convenient shorthand. Wouldn't
it be nice if instead we could write:
for (int32 index = 0; index < windowCount; index++) {
BWindow *window = windowList->ItemAt(index);
BMessage *message = new NewFunctionObjectMessage(
&WindowList::ActivateIndexedWindow, windowList, index);
BMenuItem *item = new BMenuItem(window->Title(), message);
menu->AddItem(item);
}
and get rid of the code in MessageReceived altogether. We
will try to do just that using a function object packaged
inside a BMessage .
What is a function object?
As a result of selecting our menu item we need to be able to
call a function on an object with some parameters, in our
case:
windowList->ActivateIndexedWindow(index);
A function object is an object that packages all this
information - the function code address itself, the object
we call it on and possibly some parameters. This information
is all it takes to call the function.
Since we want the user interface and the execution parts of
the application to be coupled loosely to support the
multithreaded environment well, we need to be able to
collect all that information at one point and use it to
invoke a function call at a different point. We can use the
following class to do that:
class FunctionObject {
public:
FunctionObject(void (WindowList::*callThis)(int32),
WindowList *onThis,
int32 withThis)
: function(callThis),
target(onThis),
parameter(withThis)
{ }
void operator()() const
{ (target->*function)(parameter); }
private:
void (WindowList::*function)(int32);
WindowList *target;
int32 parameter;
};
Using this class we can construct a function object:
FunctionObject *functor = new FunctionObject(
&WindowList::ActivateIndexedWindow, windowList, index);
Save it away into the functor variable and when the right
time comes, call it just like this:
(*functor)();
All the parameters (one ulong in this case) and the target
object get passed correctly, as if we called
windowList->ActivateIndexedWindow(index);
The FunctionObject class we have here would not be very
practical, because it is pretty much hard coded for this
particular example. We will need to make the class a little
bit more general. We will use templates, that way we can
adapt an appropriate function object to any target class,
member function and parameter types. Templates won't handle
the fact that we may have zero, one, two or more parameters
in a call or that some member functions need to be const so
that they can get called on a const version of the target
object. To work around that we will just use inheritance and
have a function object template for one of each of these
calling patterns, that way we will have classes representing
function objects for each of these:
function();
function(param1);
function(param1, param2);
function(param1, param2, param3);
...
We might add a flavor of each of these that has a non void
return type, but we don't really need that for the kind of
code we are trying to write. In a real world version of a
function object class we would also want const flavors that
represent invoking a const version of a member function,
taking a const target pointer. We will leave both of these
out for simplicity.
class FunctionObject {
// the abstract superclass that defines the common interface
public:
virtual ~FunctionObject() {}
virtual void operator()() const = 0;
virtual ulong Size() const = 0;
};
template<class FT, class T>
class PlainMemberFunctionObject : public FunctionObject {
// function object without any parameters
public:
PlainMemberFunctionObject(FT callThis, T *onThis)
: function(callThis),
target(onThis)
{
}
virtual ~PlainMemberFunctionObject() {}
virtual void operator()() const
{ (target->*function)(); }
virtual ulong Size() const { return sizeof(*this); }
private:
FT function;
T *target;
};
template<class FT, class T, class P>
class SingleParamMemberFunctionObject : public FunctionObject {
// function object with a single parameter
public:
SingleParamMemberFunctionObject(FT callThis, T *onThis, P withThis)
: function(callThis),
target(onThis),
parameter(withThis)
{
}
virtual ~SingleParamMemberFunctionObject() {}
virtual void operator()() const
{ (target->*function)(parameter); }
virtual ulong Size() const { return sizeof(*this); }
private:
FT function;
T *target;
P parameter;
};
template<class FT, class T, class P>
class DoubleParamMemberFunctionObject : public FunctionObject {
// function object with two parameters
public:
DoubleParamMemberFunctionObject(FT callThis, T *onThis,
P1 withThis1, P2 withThis2)
: function(callThis),
target(onThis),
parameter1(withThis1),
parameter2(withThis2)
{
}
virtual ~DoubleParamMemberFunctionObject() {}
virtual void operator()() const
{ (target->*function)(parameter1, parameter2); }
virtual ulong Size() const { return sizeof(*this); }
private:
FT function;
T *target;
P1 parameter1;
P2 parameter2;
};
...and so on.
It gets a little repetitive, but you only do it once for
your whole app. We have code to construct and call a
function object, we will now add code that packages a
function object into a BMessage on one side, unpacks and
executes it on the other side. We will use yet another class
for that:
class FunctionObjectMessage {
public:
static bool DispatchIfFunctionObject(BMessage *);
protected:
static BMessage *NewMessage(const FunctionObject *);
};
BMessage *
FunctionObjectMessage::NewMessage(const FunctionObject *functor)
{
BMessage *message = new BMessage('fCmG');
// any message with this pattern
// will be a function object
message->AddData("functor", B_RAW_TYPE, functor,
functor->Size());
// invoke the virtual Size call here to get the actual size of
// the concrete function object subclass
// need to check for errors in a real app
return message;
}
bool
FunctionObjectMessage::DispatchIfFunctionObject(BMessage *message)
{
if (message->what != 'fCmG')
return false;
// find the function object
long size;
const FunctionObject *functor;
status_t error = message->FindData("functor", B_RAW_TYPE, &functor,
&size);
if (error != B_NO_ERROR)
return false;
// function object found, call it
(*functor)();
return true;
}
We are now ready to start using function objects in our
code. First of all in every looper that receives messages we
add the function object dispatch stub that we just wrote.
Note that you will typically only need to send function
object messages against a looper, not against the individual
handlers it might have. The dispatching mechanism picks the
right function and object for you.
void
MyApp::MessageReceived(BMessage *message)
{
if (FunctionObjectMessage::DispatchIfFunctionObject(message))
return;
// not a function object, handle other messages here
...
Then we instantiate a function object for every signature of
a function we call. You can conveniently do that in a
subclass of the FunctionObjectMessage we already defined. In
our example we were trying to call a function of some
WindowList class:
class WindowListFunctionObjectMessage : public FunctionObjectMessage {
public:
static
BMessage *NewFunctionObjectMessage(void (WindowList::*func)(),
WindowList *target)
{ return NewMessage(
&PlainMemberFunctionObject<void (WindowList::*)(),
WindowList>(func, target)); }
static BMessage *NewFunctionObjectMessage(void
(WindowList::*func)(ulong),
WindowList *target, ulong param)
{ return NewMessage(
&SingleParamMemberFunctionObject<
void (WindowList::*)(ulong),
WindowList, ulong>(func, target, param)); }
};
Here we are providing a NewFunctionObjectMessage call for a
signature of each function we want to call on a given class,
in this case void (WindowList::*)() and void
(WindowList::*)(ulong)
All we have to do now is use it.
for (int32 index = 0; index < windowCount; index++) {
BWindow *window = windowList->ItemAt(index);
BMessage *message =
new WindowListFunctionObjectMessage::
NewFunctionObjectMessage(
&WindowList::ActivateIndexedWindow,
windowList, index);
BMenuItem *item =
new BMenuItem(window->Title(), message);
menu->AddItem(item);
}
This is pretty much what we tried to do in the first place.
What is the big advantage of using this approach?
Using this technique we might save some code in a
medium-to-large sized app. It is really easy to add new user
interface elements and supporting code: You write the
function that gets called when your control/menu
item/keyboard shortcut gets invoked, on the user interface
side you add code to create a corresponding function object
message, but you don't have to go and add anything on the
receiving side, in the MessageReceived function of a
corresponding handler.
Also this technique takes full advantage of C++ static type
checking support. In our example we used simple ulong
parameters, consider if you passed around some more
elaborate types. Without this technique you would be
embedding the parameters into a BMessage using
AddData /FindData and using a lot of casts. If you embedded
one type on the sending side but by mistake retrieved a
different type on the receiving side, you would not get any
compiler warnings. Using function objects you would get a
compiler error whenever you try to pass a destination
function of one type and parameters of a different type.
Pitfalls
You still have to worry about multithreading issues when
using this technique. If you dispatch a function object in
one looper that ends up calling a function/target object in
another looper, you have to make sure the other looper is
locked correctly, typically in the target function. The best
way around this is to just target the right looper by
targeting the original invoker. That way the target looper
gets locked for you as a part of calling MessageReceived .
Passing a function object to a looper in a different address
space will not work at all; the function address and the
object itself will be inaccessible from the different
address space.
Having class member functions as template parameters is a
relatively new C++ feature and the Metrowerks compiler only
supports it in the DR3 version (released with BeOS Advanced
Access Preview Release) or later. The code we used here
would not work with DR2 compilers.
Stargazing in Supportland
By Brian Mikol
Hello! Let me take this opportunity to introduce myself --
I'm Brian, one of the Be support staff. I am often the one
who replies to messages sent to custsupport, devsupport and
devservices@be.com, so some of you have already had some
interaction with me.
When I'm not running around the company searching for the
answers to your questions, I accept (and occasionally
regretfully deny) your applications to become official
registered Be developers. I also test developer's BeWare
submissions on the ftp site, and then move them into their
appropriate pub/contrib directories.
And from time to time, I get pulled into helping out on
whatever tasks need extra assistance at that particular
moment (as does every Be employee). You may have seen me at
Apple's WWDC, or you may read the few sections of the Be
User's Guide authored by yours truly (10 points if you can
guess which ones!).
Now that I have formally introduced myself, let me be a wee
bit honest. I recently returned from vacation to find a
friendly e-mail message from Valerie informing me of my
assignment to write a Be Newsletter article.
Bewildered for a moment, it hit me -- take this opportunity
to try to clarify four issues which in the past we might
have made confusing, in order to help you become informed
about Be and the BeOS. Let me start with the required Be
newsletter metaphor and go from there...
I recently went back to Massachusetts to attend my high
school reunion. While there, I spent two nights in a row
stargazing and reminiscing with an old friend. Friday night
was rather overcast, with only the occasional star poking
out here and there, but Saturday night was something else
entirely. There was not a cloud in the sky and with the air
in rural western Massachusetts, you can see EVERYTHING, from
your favorite constellation to the beautiful stream of the
Milky Way.
How does this pertain to online support at Be? I like to
look at it in the following way: Trying to find information
on some web sites is much like trying to stargaze on an
overcast night. You might find a nugget or two of
information, but for the most part, there are still
unanswered questions.
Realizing how annoying this is for customers, at Be we try
to make it as easy as stargazing on a clear night in rural
Massachusetts. We believe we have very little to gain by
keeping information from you. So before you ask your
neighbor if Orion can be seen, try looking up. You'll be
surprised at what you'll find in the sky -- or on the Be web
site!
Still, we do occasionally cloud our own skies; I can see that
in some of the e-mail messages I respond to. I'd like to
make an attempt to blow the clouds away from those issues,
and clear the skies so you can see Neptune...
- If you are applying to become a registered Be developer:
Please, be talkative! It can only help you in getting
accepted into our registered developer program. (And should
you be rejected, don't take it personally! It doesn't mean
that you can't develop for us.)
I'm referring to the "Previous Experience" and "Potential
Apps" fields here. This is the only way that we can tell
whether or not you are qualified to be a registered
developer (as much as I wish it was otherwise, I am not
psychic).
- If you are a registered developer who submits your
software to our ftp site:
Please follow the ftp submission guidelines found at the
following URL:
http://www.be.com/developers/ftp/uploading.html
If you do not, your software will not be moved from the
incoming directory. We'll try to contact you if there are
problems, but the quantity of submissions make it necessary
for our guidelines to be strict.
- "Where can I find useful information about Be and the
BeOS?"
Check our web site! I was recently talking with one of the
gentlemen from the company who will be handling
custsupport@be.com, and he was not shy in describing his awe
at the amount of information we provide on our web site.
Both Ron and Michael do a terrific job in providing as much
up-to-date information on the web site as possible -- make
their jobs worthwhile!
- "Where should I write, if I do not find my answers on the
web site?"
Here is the definitive list of Be e-mail addresses. Even
though Be is pretty small now and a single person (such as
me) might oversee more than one of the incoming support
e-mail addresses, this won't always be the case. It's best
to have everyone understand our addresses now, so there
isn't any confusion later on.
As Be grows, more people will be handling the incoming
support e-mail and it's in your best interest to send it to
the right place, as we will then be able to respond to you
more quickly. This will actually happen sooner than you
might think, as we have just hired a company to handle all
custsupport@be.com e-mail (and eventually phone calls too!).
- bedemotour@be.com
Interested in seeing the Be Travelling Circus visit your
locale? Forward any questions about hosting a Be demo day
at your school (user group meeting, etc.) to this address.
Sorry, we don't do bachelor / bachelorette parties.
- custservice@be.com
Write to this address for assistance with non-technical
customer issues. Your BeOS CD didn't arrive? Want to know
the status on your product order? BeBox service and
warranty issues? This is the place for you!
- custsupport@be.com
This is the address to which you write for help with BeBox
and BeOS technical questions which are not answered in the
Be FAQs. So for questions concerning installing,
configuring, and using the BeOS (not to mention software
or hardware compatibility), write to this address
- devservices@be.com
This address is set up to help our registered developers
with developer-specific, non-technical issues. Should you
need to change your name or company information in the
developer database, check the status of your developer
application, or have issues with your submissions to our
ftp site, write here!
- info@be.com
Write to this address for general information on Be, the
company.
- jobs@be.com
Think you're the perfect person to fill that really
awesome position at the greatest company on earth? Write
to this address!
E-mail an ASCII version of your cover letter and resume,
your e-mail and postal addresses, and both day and evening
phone numbers in the *body* of an e-mail message (attached
documents will not be considered). Don't forget to put the
title of the position you're applying for in the subject
line of your message.
- newsletter@be.com
Do you really like this article, or the newsletter in
general? Please feel free to express your enthusiasm to
the editor! Questions and comments are welcome as well.
- orders@be.com
Pssst! Wanna buy a BeOS CD? We got some "choice" Be T-shirt
to accompany that CD, as long as you're in the buying
mood...
- webmaster@be.com
This is the address to which you write if you have any
difficulties with Be's Internet-based services and
information. So if you have found a broken link, received
an error message, have a suggestion, or are having
problems with a Be electronic mailing list,
webmaster@be.com is the address for you!
So that's it. I hope this article is of help to you and has
cleared up any confusion we may have caused concerning any
of the four issues I covered. Let me know if I need to brush
up on my metaphors...
News from the Front
By William Adams
Can you spot the change from the last version?
status_t HomeDirectory(char *dir, const int buffLen)
{
app_info info;
be_app->GetAppInfo(&info);
BEntry appentry;
appentry.SetTo(&info.ref);
BPath path;
appentry.GetPath(&path);
strncpy(dir, aPath.Path(),buffLen);
dir[buffLen-1] = '\0';
return B_NO_ERROR;
}
Anyway... Blue box, yellow box, little blue box, Windows NT,
pizza, Jolt, Mountain Dew, 90 degrees, 99.999% humidity...
What does this sound like?
Mac Hack of course! You know, it's kind of funny. The
conference starts at Midnight and goes on for a few days.
You do a lot of, "How late did you stay up?", "5am". But
then you don't wake up until 12pm. You might as well just go
to bed on time and get up early, but that would not adhere
to the hacker ethic.
It was fun. Yasmin and Anita came along too. While I was
staying up bleary eyed, they were visiting the Henry Ford
Museum, and other choice places around Detroit. This
conference really is for the Mac faithful. The crowd was
split between really old timers, and really new timers. The
Woz gave his life history, which I actually found to be
interesting, maybe because it didn't end until 3am. He even
managed to slip in a controversial analysis of Steve Job's
opinion of Apple's NeXT purchase. And in his mind, Apple
should be concentrating on K-12.
Well, Benoit did a hack and gave a show. It was a real-time
Mandelbrot zoomer that had the crowd cheering even before he
showed the "fast" version. It didn't win one of the many
awards, but things like multi-colored menus did. Things just
aren't what they used to be. The take home point for me was,
we are merely guests at this party.
ftp://ftp.be.com/pub/samples/interface_kit/obsolete/mandelhack.tgz
I spent my time doing a hack as well, but I didn't feel
like submitting it because it actually might be useful.
ftp://ftp.be.com/pub/samples/drivers/obsolete/pciviewer.tgz
This thing is like PCIProbe and the poke command combined.
It will list your PCI devices in the left half of the window
using BOutlineListView , and show the actual register values
and other stuff on the right when you click on the
individual entries. For those of you who have been wanting
to use the BOutlineListView , here's you're chance. It uses
this class, as well as a custom ListItem . What great fun. I
thought about making it into a full-fledged PCI debugger
like poke is, but I ran out of time. Maybe that's an
exercise for the reader.
Well, the Preview Release is now headed for the doors and
the final anticipation can begin. You wouldn't believe how
tense things can get when a lock down is in progress. We
have a thing here called Dr. No. That's an engineer who is
assigned the unthankful task of saying "NO!" to last minute
changes so that the final release is as stable as possible,
and actually gets out the door on time. The couple of Dr.
No's did an excellent job and I think we have a really good
release headed your way. As you read the release notes you
will find that although we did a tremendous amount of good
work, you can expect there to be some anomalous behavior.
This release is a good solid foundation upon which our
future can be built.
When you do finally get it within a few weeks, code like
mad, give us feedback, and move on to commercial Nirvana.
Geoff is back from Europe, and his first e-mail said he is
anxious to write an article about the experience. So, not to
steal any steam, I'm sure you can look forward to an
enlightening expose from our American in Paris.
Where are the UI guidelines?
By Jean-Louis Gassée
I saw the question being asked in several on-line messages;
this is a good opportunity to clarify our position on the
matter of UI guidelines. First, as I write this in the
Geneva airport, and if I believe e-mail and voice-mail
messages, we're in great danger of cutting the final,
really, Preview Release CD and sending it to duplication.
The developer conference AA:PR CD gave us a great deal of
feedback, energetic and, for the most part, encouraging.
Positive or humbling, the feedback is most appreciated and
we're doing our best to reciprocate, that is, making this
developer release a credible foundation upon which to start
building useful and money-making applications. This, of
course, is for developers to decide. I rest secure, sort of,
in the knowledge we're not dealing with a shy and retiring
community.
Still with the UI guidelines in mind, the Preview Release
was a very ambitious project. Some would say we deluded
ourselves when we started, others might add this is the very
kind of denial required for starting a family, remodeling a
house -- or writing OS software. Among the features and
functions we wanted to improve, the user interface. The
graphic look, up to DR 8, was well received, but function
was found wanting. In particular, the "dock" concept didn't
get many votes. What we got, instead, were many requests for
a (now) more conventional desktop, keeping the workspaces.
Make it so, you said.
This reopened many UI issues and launched us into a great
deal of experimenting with everything from color schemes, to
text, to icons, to taskbars, to preferences... In many
respects, for the Preview Release, UI decisions were made
last. The next result is our documentation is behind the
software, itself behind schedule -- with our apologies.
Now, when it comes to UI guidelines, I understand the need
for them and, at the same time I fear them a little. I don't
want them to stifle creativity. Call this trying to put a
good face on our lateness, but after our sometimes painful
experimentation with UI function and look, I hope we'll see
some daring exploration from Be developers. You're not going
to see UI ayatollahs from Be demanding observance to UI
guidelines, published or not. I think it's a) for us at Be
to set one (by no means representing it to be the only one)
example of decent UI design and, b) for users to vote with
their wallet for what they like most, no reasons asked.
Of course, there is danger with that approach. Anarchy,
ugliness, confusion. As for anarchy, I firmly believe
developers will do what they want, anyway. The meek will not
inherit our region of the Earth. For ugliness, it disappears
quickly, unless the product is incredibly useful, or
inexpensive. All of which is fair. Lastly, confusion. In the
early days of bit-mapped screens and pointing devices,
perhaps, when customers, even sophisticated ones, were
unfamiliar with the emerging paradigms, sorry, ways.
No such problem anymore.
Still, no excuses, we need to, and will publish UI
guidelines, if only to provoke creativity and observance in
the breach.
|