C/C++ Contributing Editors


Uncaught Exceptions: 106 Miles to Vancouver

Bobby Schmidt

A const conundrum and how to weasel out of it.


Copyright © 2002 Robert H. Schmidt

They’re putting the band back together.

By the time you read this, Halloween will be only a few weeks away. If your Samhain dance card is still empty, may I recommend a date with The C++ Seminar #3 [1] in Vancouver, WA [2]. The setting this time is The Heathman Lodge, which I’m told looks like a timber-hewn Swiss mountain retreat.

Continuing their tradition, the Fab Five [3] will perform new material never before seen in decent society. In addition, I’ll be driving down to play moderator (translation: ringmaster) for the panels. I thoroughly enjoyed TCS 1, was sorry to miss TCS 2, and very much look forward to TCS 3. If you are fans of the presenters, or just want concentrated time with fellow C++ophiles, I encourage you to attend.

Mem Fun for the Whole Family!

Q

Hello,

I hope you find this an interesting question. I’d really like to understand this area better, and I’ve not had much luck finding an answer.

Here’s my situation:

I have a container class C with a private STL vector member v. The vector holds objects (not pointers) of element type E. Some of the container functions just go through the vector and call a member function for each item in the vector. I’m using the STL algorithm for_each to iterate over the vector.

At first, I had written an intermediate function that called the member function and then passed that intermediate function to for_each. But then I found I could use the pointer-to-member function adapter mem_fun_ref to do the same thing. This code works fine and now looks like:

void C::show_each()
   {
   for_each(v.begin(), v.end(),
      mem_fun_ref(&E::show));
   }

The problem comes when I have a similar situation, but the member function takes a parameter. Herb Schildt’s STL from the Ground Up (Osborne McGraw-Hill, 1998, p. 283) has an example just like this — the example uses the bind2nd binder to force for_each to pass the parameter with each function call. But one difference is that the vector contains pointers to the objects, not copies of the objects, and it uses the adapter mem_fun1.

I’m trying to adapt the code to use mem_fun1_ref without any luck. Here’s the non-compiling code, which shows error messages in Microsoft Visual C++ 6.0:

void C::show_each_in(int width)
   {
   for_each(v.begin(), v.end(),
      bind2nd(mem_fun1_ref
         (&E::show_in), width));
   }

Any idea what gives? It seems the bind2nd binder doesn’t work with the object references, but is there a substitute that does? Or is the problem something completely different? Thanks for any help or advice you might be able to give!

— Dave Gustafson

A

Since you don’t provide me any additional code, I’ve had to make some educated guesses toward reconstructing your original problem. I’m pretty sure I now know what is happening; at the very least, I can reproduce your compiler diagnostics.

I’ve kept the logic of your code fragments intact, but altered the names for publication. From those fragments I’ve extrapolated the complete program that appears as Listing 1. That program, when compiled with Visual C++ 6.0 SP 4, produces the diagnostic in Listing 2.

When I first saw this diagnostic, it took me a few moments to connect the dots:

While the diagnostic’s immediate cause is mem_fun1_ref_t’s operator() trying to call a non-const member on a const E, the underlying cause is binder2nd’s operator() assuming and allowing only const arguments. Both binder1st and binder2nd suffer this same limitation, one that the C++ committee considers a defect in the Standard [4]. I expect that by the time the next standard is approved — and possibly before that, in a Technical Corrigendum — the committee will have fixed this defect.

After changing E::show_in to be a const member, I’m left with one new diagnostic:

test.cpp(42) : error C2664: 
'mem_fun1_ref' :   cannot convert
parameter 1 from 
void (__thiscall E::*)(int) const
to void
(__thiscall E::*)(int)'

Types pointed to are unrelated; 
conversion requires reinterpret_cast, 
C-style cast or function-style cast

mem_fun1_ref is not part of the C++ Standard library, but rather is a non-standard extension particular to the Dinkumware implementation [5]. That extension is defined essentially as:

template<typename R, typename T, typename A>
mem_fun1_ref_t<R, T, A>
mem_fun1_ref(R (T::*m)(A))
   {
   return (mem_fun1_ref_t<R, T, A>(m));
   }

mem_fun1_ref can adapt only a non-const member. Asking it to adapt a const member leads to the diagnostic. To adapt a const member function, mem_fun1_ref would need a slightly different definition:

template<typename R, typename T, typename A> /*
vvvvv*/
const_mem_fun1_ref_t<R, T, A> /*
^^^^^                     vvvvv*/
mem_fun1_ref(R (T::*m)(A) const)
   {     //vvvvv          ^^^^^
   return (const_mem_fun1_ref_t<R, T, A>(m));
   }     //^^^^^

You’re caught in a dilemma:

The Standard seems to offer hope through mem_fun_ref, which is specified to come in four flavors:

//
// non-const member with no arguments
//
template<typename R, typename T>
mem_fun_ref_t<R, T>
mem_fun_ref(R (T::*m)());
//
// non-const member with one argument
//
template<typename R, typename T, typename A>
mem_fun1_ref_t<R, T, A>
mem_fun_ref(R (T::*m)(A));
//
// const member with no arguments
//
template<typename R, typename T>
const_mem_fun_ref_t<R, T>
mem_fun_ref(R (T::*m)() const);
//
// const member with one argument
//
template<typename R, typename T, typename A>
const_mem_fun1_ref_t<R, T, A>
mem_fun_ref(R (T::*m)(A) const);

The second overload is equivalent to mem_fun1_ref. The fourth overload seems to be what we want and should allow:

bind2nd(mem_fun_ref(E::show_in), width)

You can probably guess the punch line: the Visual C++ 6.0 library does not properly support this overload set. Of the four overloads, the library defines only the first one.

Ultimately, then, you can’t do what you want with the stock Visual C++ 6.0 library. Your useful choices seem to be these:

If I change the two places I’ve indicated — declaring E::show_in const, and calling mem_fun_ref instead of mem_fun1_ref — the program in Listing 1 compiles and runs correctly on all of my other test systems. In addition, the latest libraries from Dinkumware and Metrowerks add an extra non-standard operator() to binder1st and binder2nd. These extra overloads let the binders work with non-const member functions, in accordance with how I expect the Standard to evolve.

The above discussion involves MEMber FUNctions invoked on REFerences to objects; that’s why the adapter names all start with mem_fun_ref. The same ideas apply to member functions invoked on pointers to objects (mem_fun...) and non-member functions (ptr_fun...). Scott Meyers discusses the three adapter families in Item 41 of his Effective STL [6].

It’s a long<N> World, Revisited

Q

(In reference to “It’s a long long long long world” in your July column.)

Why do all answers to this issue ignore templates?

In this case, the question suggests s8, u128, etc; the answer suggests intN_t.

The approach taken by many people who model hardware is to define templates, such as Uint<N>. One example of this, which seems to be becoming accepted, is the SystemC.org framework.

I’ve never seen this problem solved as templates outside of the hardware modeling community. Is there a good reason why this seemingly obvious approach is not used more widely?

— Dave Whipp

A

(For those who tuned in late: in my July column, I answered a question from Tony Clifton, who wondered about possible Standard-sanctioned names for C++ integer types beyond long. I surmised that any solution would come in the library instead of the language, and that the C99 library offered a likely direction.)

I had considered templates while crafting that column. Indeed, I offered a template solution to this very problem way back in my January 1998 column. I ultimately decided not to bring up templates for three reasons:

I want to briefly explore this last point here.

Assume you want a template that wraps exact-size integers. One simple and simplistic implementation is:

template<size_t bit_count>
class Int
   {
   };

template<>
class Int<8>
   {
private:
   typedef signed char native;
public:
   Int(native n = 0) : n(n)
      {
      }
   operator native &()
      {
      return n;
      }
private:
   native n;
   };

template<>
class Int<16>
   {
private:
   typedef short native;
public:
   Int(native n = 0) : n(n)
      {
      }
   operator native &()
      {
      return n;
      }
private:
   native n;
   };

and so on, for all of the native integer sizes available. You can use these types in lieu of real integers in many contexts:

Int<16> i16 = 111;
int i = i16;
i16 = 222;
++i16;
cout << i16;

But then consider:

Int<21> i21;
Int<21> *p21;

What does this mean on a system lacking 21-bit integers? Should such declarations be allowed? If not, how do you completely and elegantly enforce that rule? Or should Int<21> round up/down to the nearest native size?

I’ve never seen the SystemC code [7], so I don’t know if it addresses these problems.

Overall, if you care about neither C portability nor (likely) C++ library conformance, and if you find a satisfactory answer to the Int<21> problem, then templates are a quite valid approach, one in many ways superior to the C99 typedefs.

Spatial Anomaly in my Inbox

The saga of the DOS parallel port reaches an ironic conclusion.

Last time I cited several Diligent Readers for their help in this cause and promised to summarize their replies. Today I can find neither their messages nor the notes I had put together from those messages; even my usually reliable backups are no help. Just as the original question fell from another reality into this one, my answer has vanished, possibly back to the question’s home continuum.

I suppose I could solicit help again or try researching this on my own. I could, but I won’t, for there’s clearly a Deeper Truth at work here. Like the Grail perched tantalizing out of Indiana Jones’s grasp [8], there are some things we are allowed to hold briefly yet are never meant to keep.

Erratica

My May column item “Mystery Date” contains this simple exception-handling sequence:

try
   {
   // exception thrown...
   }
catch (std::exception)
   {
   }
catch (int)
   {
   }
catch (...)
   {
   // ...and caught here
   }

It also contains a typo. As Diligent Reader Jonas Meyer Rasmussen points out, the first catch clause should be:

catch (std::exception &)

or more likely:

catch (std::exception const &)

Otherwise the original thrown exception is needlessly copied and sliced.

Notes

[1] <www.thecppseminar.com/03/ index.htm>

[2] Not to be confused with Vancouver, BC. Vancouver, WA is across the Columbia River from Portland, OR.

[3] For the uninitiated, that’s Andrei Alexandrescu, Steve Dewhurst, Scott Meyers, Dan Saks, and Herb Sutter.

[4] http://anubis.dkuug.dk/jtc1/sc22/ wg21/docs/lwg-defects.html#109

[5] The Dinkumware library bundled with Visual C++ 6.0 was first published in 1995, three years before the C++ Standard froze. I suspect that mem_fun1_ref was an interim part of the library that didn’t make the final cut.

[6] Scott Meyers. Effective STL (Addison-Wesley, 2001), pp 173-7. And yes, the mem_fun family should be called the mem_fun_ptr family for consistency with mem_fun_ref. But mem_fun was used first, and the name stuck. The library people on the standards committee are funny this way. In an apparent policy of stare decisis, they in some ways abide by precedent; yet in other ways — templatizing library elements, moving elements into namespace std — they radically disregard precedent.

[7] <www.SystemC.org/>

[8] <http://us.imdb.com/Title?0097576>

Although Bobby Schmidt makes most of his living as a writer and content strategist for the Microsoft Developer Network (MSDN), he runs only Apple Macintoshes at home. In previous career incarnations, Bobby has been a pool hall operator, radio DJ, private investigator, and astronomer. You may summon him on the Internet via BobbySchmidt@mac.com.