Thursday, April 10, 2008

C++ array size determination - Part 2

In my last post I showed how to create a template function to determine the size of a C++ array and promised to show how to create a template that will work with a type instead of a variable.

Lets start by looking at a struct template that through specialization we can use to capture the number of elements in an array.
  template <typename T>
struct array_info
{
};
This just names a template that we will specialize for arrays, like so:
  template <typename T, size_t N>
struct array_info<T[N]>
{
typedef T type;
enum { size = N };
};
Now we have a template that for arrays will enable us to get to the size and type of elements in the array. This can be used like so:
  typedef int IntArray[10];
size_t s = array_info<IntArray>::size; // s == 10
Or like so:
  float floatArray[array_info<IntArray>::size];
If we try something other than an array we get a compiler error.
  size_t s = array_info<int*>::size; // error
Generates a compiler error similar to:
  error: 'size' is not a member of '<unnamed>::array_info<int*>'
This is nice, but the real power is in using this with other templates. Even though I'm working with arrays one of the apis I'm using returns a pointer instead of an array reference. I know the type (which includes its length) so I wanted to take advantage of that. Here I use the array_info in an equal method to determine the length:
  template <typename A, typename T>
bool equal(const T* lhs, const T* rhs)
{
// Use 'typename array_info<A>::type' instead of 'T' so that compiler
// verifies A and T types match up.
const typename array_info<A>::type* const end = &lhs[array_info<A>::size];
return std::equal(&lhs[0], end, rhs, &equal_to<T>);
}
This template can then be used like so:
  equal<IntArray>(lhs.getVal(), rhs.getVal());
That is not syntax you get to use everyday.

C++ array size determination

My current project has me working with C-style C++ arrays. In order to make things easier and safer I created some helper templates.

Lets start with finding the the number of elements in an array. The conventional C way would be to use sizeof like:
  sizeof(array)/sizeof(array[0])
I'm not a big fan of this approach. I have seen production code by seasoned C++ developers that looks like this:
  const char* tmp = 0;
// ...
int size = sizeof(tmp)/sizeof(tmp[0]);
Of course this is sometimes hidden behind a macro:
  #define array_size(array) (sizeof(array)/sizeof(array[0]))
// ...
int size = array_size(tmp);
Either way, this gives the wrong answer. The developer, of course, is not interested in what the size of a pointer to an element divided by the size of an element is.

So, can we do better? Actually, yes, we can.
  template <typename T, size_t N>
inline
size_t array_size(const T (&lhs)[N])
{
return N;
}
Here we pass an array by reference to array_size() that extracts the size of the array and returns it. I talked briefly about array passing a few years ago.

We can now use it like so:
  int ia[10];
size_t s = array_size(ia); // s == 10
And if we try to use this with a pointer like so:
  const char* tmp = 0;
// ...
size_t s = array_size(tmp); // error
We get a compiler error similar to:
  error: no matching function for call to 'array_size(const char*&)'
Very nice.

However, sizeof can also be used with a type instead of a variable. In my next post, I'll show how we can create a template that will work with a type instead of a variable.

Wednesday, April 02, 2008

BoostCon'08

I'm speaking on Boost.Thread this year at BoostCon'08.

Here is the announcement from the Boost mailing list:

Reminder: early registration for BoostCon'08 closes Monday, April 7.
It's still not too late to avoid the late registration fee for what may
be the finest C++ event of 2008.

For the 2nd annual Boost C++ libraries conference, we've put together a
fantastic program crowned by a keynote address from Bjarne Stroustrup.
In addition to existing Boost libraries, we're covering technology of
interest to any C++ developer trying to stay on the cutting edge,
including hands-on sessions with features from the upcoming 2nd version
of the C++ standard. This year we've also added a collection of short
"author's corner" sessions for those of you who want an inside
perspective on how advanced libraries are developed. See
http://www.boostcon.com/program for details.

BoostCon 2008 will be hosted at Aspen Center for Physics, one of the
most beautiful meeting sites in the world, and a great venue for
collaboration and discovery. The combination of a relaxed pace and
intense inquiry made BoostCon'07 an event to remember, and we expect no
less for this year. Please visit http://www.boostcon.com/registration
to register.

Thanks!

-- The BoostCon Planning Committee

David Abrahams
Beman Dawes
Jeff Garland
Joel de Guzman
Eric Niebler
Sean Parent
Jeremy Siek
Matthias Troyer

Monday, March 31, 2008

Building Boost 1.35.0

Seems I always have troubles building Boost.

To start I followed the big 'Get Boost' button on the right side of boost.org; thinking, 'That sure is obvious'. That link takes you to a download page with boost version 1.35.0 right at the top. It shows the new libs with 'Detail' and 'Download'. I took a few seconds to read the new libs section and clicked on download. This took me to sourceforge.net where they have the source code only release. Well, I knew that Boost Consulting has maintained an installer for Windows assuming you are using boost with Visual Studio. So I jumped over to their boost installer site, only to find that they don't have one yet for 1.35.0.

No problem, I'll just go back and grab the source code from sourceforge.net and follow the instructions in the getting started document. I kicked off the download and started looking for the getting started document. After a while, I figure I must be crazy, because I can't find it. I finally do a google search in order to find it. Still thinking I must have just missed it, I went back and followed every link on the right of the the main boost site. It is not under 'Introduction' or any other of the sections. I finally did locate it, it is on the main page on the left under 'GETTING STARTED'. Funny how you can look right past something like that.

So now I figure all I have to do is follow the getting started guide to build boost.

Step 1. Get Boost. I already did that. My download had already completed from sourceforge.net. I unziped it (actually un-7zed it) to c:\boost\boost_1_35_0\.

Step 2. Explain Boost Distribution. Ok, nothing new here.

Step 3. Header only libs. Right, header only libs are nice. Just compile and go. However, Boost.Thread among others need to be compiled.

Step 4. Build with a header only lib. Been there done that many times. There are sub-steps for building with Visual Studio. I always use MPC, so this is not useful to me. I plan to create a separate post or two about building boost applications with MPC. When you see how easy it is to use MPC with Boost, I doubt that you will use anything else.

Step 5. Building Boost libraries. Alright, here we go.

Step 5.1. Install Boost libraries. Yeah, thats what I wanted to do to start with. However, Boost Consulting has not yet put out an installer for 1.35.0.

Step 5.2. Build from source.

Step 5.2.1. Get bjam. I put the executable in c:\boost\boost_1_35_0\.

Step 5.2.2. Identify toolset. I want: msvc.

Step 5.2.3. Select a build directory. It says this is not necessary.

Step 5.2.4. Invoke bjam. This is where I got in trouble. I tried various combinations of commands/arguments to bjam. I must have tried to build 3-4 times. Finally I settled on the following after looking at bjam --help.
cd c:\boost\boost_1_35_0
bjam --build-type=complete --prefix="c:\boost\boost_1_35_0" install
Seems like I should have included --toolset=msvc, but I forgot and it picked msvc-8.0 anyway.

This worked for me creating a c:\boost\boost_1_35_0\lib directory as the rest of the getting started document assumes. Actually the getting started doc has it under C:\Program Files\boost\boost_1_35_0\lib, but I like c:\boost\boost_1_35_0\lib better.

After the build you can delete the c:\boost\boost_1_35_0\bin.v2 directory to free up space.

Friday, March 21, 2008

Windows XP Daylight Savings Time Problem

On March 11, 2008 I had to turn off Windows XP automatic synchronization with an internet time server because the time was wrong on my laptop. I manually set the time and forgot about it. Yesterday I realized that my email message times were off by one hour. I looked at one of my sent emails and it had "Date: Fri, 21 Mar 2008 14:39:48 -0600". I googled and figured out that since I'm in CDT this should be -0500. As a matter of fact emails sent to me from those I work with did in fact have this in the message header: "Fri, 21 Mar 2008 14:34:36 -0500 (CDT)". I couldn't figure out why my machine wasn't updated for the new daylight savings time begin date. I have all the latest Windows XP updates.

I talked to our system admin at OCI and he said I needed to apply this update:928388. I downloaded it, but it said that it was already applied. After digging around some more I found the following "How to configure daylight saving time for Microsoft Windows operating systems". I followed the steps which require you to create and apply a registry file and create and run a vbs script. That fixed the problem.

Monday, March 10, 2008

std::for_each with C-style arrays

Although std::for_each is most often used with the standard containers, it can also be used with C-style arrays. Take for example the following:
#include <algorithm>
#include <functional>
// ...
WriterTask writer[NumThreads];
std::for_each(writer, writer+NumThreads,
std::mem_fun_ref(&WriterTask::activate));
// ...
std::for_each(writer, writer+NumThreads,
std::mem_fun_ref(&WriterTask::join));
Or if you prefer:
std::for_each(&writer[0], &writer[NumThreads],
std::mem_fun_ref(&WriterTask::activate));
Compare the above with the explicit for loop:
for (int i = 0; i < NumThreads; ++i) {
writer[i].activate();
}
I'm not completely convinced that the std::for_each case is more readable. However, I liked it better than the for loop for some test cases I was writing. It collapsed a number of subsequent for loops down into one line statements making it clearer the intent of the test.

Friday, February 29, 2008

std::min and std::max

Today I typed the following:
int t = (std::max)(timeout, lagtime);

Why did I put parentheses around std::max? Because windows.h defines (among other things) a max and a min macro. If you include windows.h the above code will not compile. For example the following:
#include "windows.h"
#include <algorithm>

void foo() {
int i = 5;
int j = 7;
int x = std::max(i,j);
}

Will produce the following error with Visual Studio C++ 2005:
1>test.cpp(7) : error C2589: '(' : illegal token on right side of '::'
1>test.cpp(7) : error C2143: syntax error : missing ';' before '::'

There are a number of ways to work around windows.h defining these two macros.

  • Use alternative names defined in windows.h.
    int x = _cpp_max(i,j);
    int y = _cpp_min(i,j);
    This is not portable; only works on Windows.

  • Define NOMINMAX before including windows.h. This might break existing code that assumes NOMINMAX is not defined.

  • Don't use std::min and std::max. Instead use the tertiary operator like so:
    int x = i > j ? i : j; // max(i,j)
    int y = i < j ? i : j; // min(i,j)
    This is portable but not as readable and more error prone.

  • Use using statements to make the code portable:
    using std::min;
    using std::max;
    int x = max(i,j);
    int y = min(i,j);
    This works but requires two more lines of code. You could also just use 'using namespace std;' but that might pull in more than you want.

  • Use std::min<int> and std::max<int>
    int x = std::max<int>(i,j);
    int y = std::min<int>(i,j);
    This requires you to specify the type. However in some cases this actually helps. For example:
    int i = 5;
    unsigned int j = 7;
    int x = (std::max)(i,j);
    int y = (std::min)(i,j);
    Note the 'unsigned'. Generates the following errors:
    1>test.cpp(7) : error C2780: 'const _Ty &std::max(const _Ty &,const _Ty &,_Pr)' : 
    expects 3 arguments - 2 provided
    1> c:\program files\microsoft visual studio 8\vc\include\xutility(3190) :
    see declaration of 'std::max'
    1>test.cpp(7) : error C2782: 'const _Ty &std::max(const _Ty &,const _Ty &)' :
    template parameter '_Ty' is ambiguous
    1> c:\program files\microsoft visual studio 8\vc\include\xutility(3182) :
    see declaration of 'std::max'
    1> could be 'unsigned int'
    1> or 'int'
    By explicitly specifying type via <int> you remove the ambiguity.

  • Use (std::min) and (std::max)
    int i = 5;
    int j = 7;
    int x = (std::max)(i,j);
    int y = (std::min)(i,j);
    This works (as does the std::max<int>) because the C++ preprocessor requires '(' as the next preprocessing token following the macro name to preform the macro expansion.

Thursday, February 28, 2008

C++ if variable initialization

All C++ programmer know that a variable can be initialized within the definition of a for statement like so:
for (int i = 0; i < 9; ++i) {  
std::cout << i << '\n';
}
However, many do not know that the same can be done in an if statement. For example:
if (Foo* f = dynamic_cast<Foo*>(a)) {
f->doSomething();
}

// f is not available here.
You should always define your variables to have the smallest possible scope. This technique allows you to pull your local if variables into the if statement thereby reducing their visibility.

Monday, February 25, 2008

std::fill_n

Many of the tests I have been writing lately involve creating min/max/misc. values to send across our messaging system. It dawned on me today that the for loops I have been writing could be better expressed with std::fill_n. For example take the following loop:
std::vector<std::string> strSeq;
const int MAX_STRSEQ_SIZE = 31;
for (int i = 0; i < MAX_STRSEQ_SIZE; ++i)
strSeq.push_back("0123456789");
This can be better written using the standard template library:
std::vector<std::string> strSeq;
const int MAX_STRSEQ_SIZE = 31;
std::fill_n(std::back_inserter(strSeq), MAX_STRSEQ_SIZE,
"0123456789");
Although this isn't really any shorter than the original for loop, I believe it reads better.

Lets review std::fill_n():
template<typename ForwardIter, typename Size, typename T>
fill_n(ForwardIter begin, Size n, const T& value);
std::fill_n takes three arguments: ForwardIter, Size, and T. ForwardIter is designed to take an iterator pointing to where to begin filling. However, since std::fill_n assumes there is room in the container we use std::back_inserter() to adapt the assignment operator into a push_back on our container. Size is how many of T to assign and T is the value to assign.

Friday, February 22, 2008

Next-Gen PC Design Competition

I you have not already done so, hop over and check out some of these great designs and vote for you favorite.

Next-Gen PC Design Competition

I voted for the FluxPC.

Wednesday, February 20, 2008

Providence Christian Academy Website

Since moving to Pensacola in June 2007 my kids old school, Providence Christian Academy has redone their website from when I maintained it. Here is a link to the old version Providence Christian Academy. I think the current one is better than the one I hacked together.

What do you think?

Monday, February 11, 2008

Internet Browser Crashing

Update:
It now appears re-installing Java did not fix my problems. My crashes are back and they are worse than ever. I'm beginning to think it may be a hardware problem. I booted with a Knoppix Linux CD and tried Firefox from there. It also crashed multiple times.

Original Post:
For many months I have suffered from an unstable Windows XP. Firefox, IE, Safari, Opera, Thunderbird, Windows Media Player, QuickBooks, and every other program that dared attempt to access the internet would periodically crash (Microsoft Debug Window) or simply just disappear. If I attempted to debug using Visual Studio 7.1 or 8, Visual Studio would crash with a Microsoft Debug Window (not very helpful). I looked in the event viewer many times but never found anything. I ran many different kinds of virus and spyware removal tools. Nothing could be found to fix my problems. I, of course, Googled my problem many times and never found anything.

The most annoying thing was doing my online banking (and having the browser crash while trying to purchase something). I was unable to access Bank of America from any browser except Safari. Safari would also crash, just less often. Other browsers might work for a few pages but would always crash more often than Safari. IE was the worse. I could hardly go to any pages without IE crashing. Firefox (my browser of choice) was mostly stable. It however would crash multiple times a day. This was bearable because Firefox would restart exactly where I left it with all my tabs ready to go.

I kept telling myself that I would re-install Windows XP when my current project ended. I didn't want to risk making it worse since I use VSClient and VNC everyday to access a remote computer for work. VNC never crashed.

However, the other day I started doing my taxes. I have used TurboTax online for many years. So I fire up Firefox and access TurboTax only to have it crash almost immediately. Same for Safari. Ugh! It was the straw that broke the camels back. I dug out my Windows XP CD and kicked off a re-install. Hours later, I finally have Windows XP reinstalled. This was after having to call Microsoft to activate Windows XP since the activation window crashed trying to access the internet. Not a good sign. And as you may have guessed by now, this didn't help one bit. The only difference I saw was that the icons in Bloglines have now disappeared; replaced with the text 'expand folder' and 'collapse folder'.

Now I'm really frustrated. I just knew the re-install of Windows XP would fix the problem. At this point I'm starting to think the only remedy will be to start from scratch with a clean install of Windows XP (or maybe Linux). However, I use Visual Studio and QuickBooks and other Windows only applications.

Back to Googling. I ran across this site: IE crashes at certain web-sites. It recommends re-installing Java. So I follow their link and grab Java and re-install it. This seems to fix most, but not quite all, of my problems. I can now access TurboTax and Bank of America from Firefox without it ever crashing.

I re-installed Java about a week ago. Firefox has only crashed on me twice since then. Thunderbird has crashed a few times (this might not be related, not sure). QuickBooks now can access the internet (very important since I had to register it). Windows Media Player has not crashed since. And IE seems to actually work now. I use Firefox almost exclusively, however. So I think I'm mostly back to normal. I assume other folks have a few crashes here and there.