Departments


We Have Mail


Letters to the editor may be sent via email to cujed@cmp.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.


Marc,

Thanks for continuing the tradition of excellence in C/C++ Users Journal which Mr. Plauger (and others) has established over so many years. I have a question regarding Scott Meyers' article on non-member functions and encapsulation ["How Non-member Functions Improve Encapsulation," CUJ, February 2000]. Perhaps Mr. Meyers could respond.

Using his definition for comparing degrees of encapsulation (which seems reasonable), he asserts that functionality which can be implemented as a non-friend, non-member (NFNM) function would be preferred over a member (or even static member) function.

Mr. Meyers states that a class with n member functions is more encapsulated than if it has (n+1) member functions. However, if a class has n member functions plus functionality which must go in either a NFNM function or a member function, there are (n+1) total functions under consideration. Using his analysis, I don't believe that where they are implemented impacts how one evaluates the degree of encapsulation. (If it does, I think there is a circular argument.)

Further, if the implementations of the NFNM and member function are identical (i.e., both in terms of the public interface of the class), then neither is more fragile than the other with respect to a class implementation change. If such a change breaks the NFNM function, it would also break the member function; and conversely, if the change didn't break one, it wouldn't break the other.

By Mr. Meyers' definition, then, the encapsulation of either approach would be equivalent. In such a case, I'd personally prefer, using his example:

w.nap();   // more like w.sleep(.5);

to:

nap(w);

Am I missing something here?

Thanks,

Donnie Hale
dhale@columbus.rr.com

Scott Meyers replies:

The measure of encapsulation is not how many functions WILL be broken when a class is changed, it's how many functions MIGHT be broken if a class is changed. When you modify the implementation (only) of a class, you must verify that all member functions continue to work correctly. With non-member non-friend functions, no such verification is necessary. Even if a member and a non-member use only the public interface of a class, the language gives you no way of knowing that. Practically speaking, you still have to test the member if the class is changed. Not so with the non-friend non-member.

Scott Meyers


Dear CUJ,

There appears to be a bug in "Not-So-Obvious Utility Macros" [by Radoslav Getov, CUJ, March 2000]. The constructor of UniversalStorage_T creates two instances of the makeDelayedAssigner_T class. One is destoyed immediately, and another is destroyed when the destructor of UniversalStorage_T itself is called. The effect is that the value of the saved variable is being "restored" twice, once during the saving and another time when the saver object goes out of scope. So the following printf call will be executed (when it shouldn't):

int i = 0;

void AssignTest()
{
    DELAYED_ASSIGN (i, 1)
    if (i != 0)
    {
    // line broken to fit column
    printf
       ("DELAYED_ASSIGN modified"
        "variable immediately !\n");
    }
}

The fix would probably require rewriting the destructor of makeDelayedAssigner_T or introducing yet another helper class, further inflating the macro.

Also notice that some macros use static variables. These days you can't afford to force your code to be single threaded. Even many embedded systems support multithreading. Someday, someone will reuse your code in a multithreaded environment, expecting it to work the way functions like strlen work regardless of threading model. So you can't use static variables and be proud of it.

Thanks

George Datuashvili

Radoslav Getov replies:

Dear George,

I don't think that the problem with immediately assigning the saved value back is a bug. My assumption is that there was nothing wrong in it except for some performance penalties. On the other hand, the entire SAVE macro idea is to have usage as simple as possible, even at the expense of some performance.

You are right about the multithreading problem, though. I didn't want to go into to this matter, both because it would both complicate the code and hide the basic ideas and because the multithreading support is so different on each platform. I wanted to use only standard C++.

I hope that anybody who's been using multithreading can add the necessary locks to make the code thread safe. I think I should have mentioned this in the article, but it's too late now :-)

Best Regards,

Radoslav


Dear CUJ,

While I enjoyed the article "Just Another Version of Algol" by Mr. Plauger (CUJ, January 2000), I wonder why "Interfaces and Inner Classes" by Chuck Allison was included. By calling your publication C/C++ Users Journal, I would sort of expect articles to be focused on C++. If I cared one whit about Java I would subscribe to a journal that focused on that language.

Matthew Wilson

Chuck's charter is to write about things in Java that are of potential interest to C/C++ programmers. Java interfaces are closely related to abstract classes in C++; likewise, Java's inner classes are related to nested classes in C++, although they are admittedly a different kind of animal. I think it is only natural for some C/C++ programmers to be interested in how constructs such as these work in other languages. Rather than hinder our process of mastery, I believe that paying a little attention to another, closely related language can help us deepen our understanding of C and C++. In other words, you can't put an airtight box around curiousity. I do appreciate your feedback, though. Thanks for writing. — mb


Hello all,

I would like to say that as a subscriber of C/C++ Users Journal I found the article about programming Excel in C++ using COM ["Programming Excel COM Objects in C++," CUJ, April 2000] very interesting and immediately useful for on-going projects in my job.

I downloaded and attempted to run the source code that accompanies the article. I found a couple of hang-ups that most likely found their way in during cropping process (to make the source code fit onto a printed page).

The #import directives need to appear in the source code without the extra line breaks that appear in the source code currently available online.

Also, the following source code fragment specified Psheet instead of pSheet:

//— Was Psheet!!!
pSheet->Range[Cells(1,i)]->Value =
   ColName;
// Set the values in the table
for (j=1; j<=Lin; j++)
{
   if (1 == i)
   {
      // Set the row titles
      RowName[4] = '0'+j;
      //— Was Psheet!!!
      pSheet->Range[Cells(j,1)]->
         Value = RowName;
   }
   else
   {
      ...
   }

I have attached the code with these revisions. This code will compile successfully in Visual Studio.

Thanks again for the excellent article!

Kevin Wittmer
Motorola Chicago, IL

Our standard page layout tends to create havoc with long source lines, especially when the code being displayed involves non-standard syntax rules, as is the case with VC++ #import directives. We have not even attempted to reproduce the correct form here (which you were kind enough to provide), since we would just end up butchering it again. We have updated the original source code on our ftp site (ftp.mfi.com/pub/cuj/2000/lacoude.zip; also accessible through http://www.cuj.com/code/archive.html). Our apologies to anyone who was inconvenienced by this error. — mb


Hi,

This is with respect to the "Better Assertions for MFC" article in the June 1999 issue of your magazine [by Giovanni Bavestrelli]. It's a pretty nifty trick, and I think that the double expression evaluation drawback can be circumvented as follows:

template <class T> inline T
Foo(T & Result)
{
   if(!Result)
   {
      // Do the error handling stuff here
   }

   return(Result);
}

#ifdef NDEBUG
#define MYASSERT(Expression) Expression
#else
#define MYASSERT(Expression) Foo((Expression))
#endif

This paves the way for expressions like:

if(MYASSERT(pBuffer))

or

lstrcpy(MYASSERT(pBuffer),
   "This is a test");

or even:

MYASSERT(pWnd)->DoSomething();

Atul Khare

Giovanni Bavestrelli replies:

Dear Mr. Khare,

Thank you very much for your comments. I think the solution you suggested is going in the right direction, but it has a few problems:

1) You cannot pass any constant object to it. 2) It returns a copy of the object of type T, and making a copy is not always possible or correct (i.e., T might not have a copy constructor, and in some situations you might end up working on the copy instead of on the original object). 3) The arguments to pass filename and line are missing (though you probably just omitted them for simplicity).

Incidentally, the solution that was suggested by another reader and that I proposed in my response to Andrei Alexandrescu's mail (CUJ, August 1999) has similar problems. I think the best solution I have seen was the one I received from Roman Kononov. It is the solution I was looking for while writing the article, but unfortunately I could not find it, as it does not work with my compiler, Microsoft Visual C++. Here it is:

#ifndef NDEBUG

template<class T> inline T&
safe__(T& val, char const* txt,
   char const* file,int line)
{
// T::operator!() ->
// possible side effect
   if (!val)
      _assert(const_cast<char*>(txt),
         const_cast<char*>(file),
            line);
   return val;
}

template<class T> inline T const&
safe__(T const& val, char const* txt,
   char const* file, int line)
{
// T::operator!() const ->
// possible side effect
   if (!val)
      _assert(const_cast<char*>(txt),
         const_cast<char*>(file),
            line);
   return val;
}

#define SAFE(exp) \
   safe__(exp,#exp,__FILE__,__LINE__)

#else

#define SAFE(exp) (exp)

#endif

Regards,

Giovanni Bavestrelli