Just a quick note, I thought others might find this helpful as well. In our level compiler, a bug which somehow went un-noticed for a little while was causing bounding boxes to not be calculated properly. This turned out to be a simple initialization oversight.
Vec3 maxValue, minValue;
ofcourse the default constructor would initialize both vectors to [0,0,0]. This is useless when trying to determine a bounding box which lies entirely in the negative part of all axies (because no vertex in that area will ever excede maxValue, which has incorrectly been initalized to zero). The fix was simple, but it reminded me of another C/C++ quirk over numeric limits.
The fix, one would think, would be something like this:
const float FLOAT_HIGHEST = std::numeric_limits<float>::max();
const float FLOAT_LOWEST = std::numeric_limits<float>::min();
Vec3 maxValue(FLOAT_HIGHEST, FLOAT_HIGHEST, FLOAT_HIGHEST);
Vec3 maxValue(FLOAT_LOWEST, FLOAT_LOWEST, FLOAT_LOWEST);
But, you’de be wrong. If you were using numeric_limits<int> that would work fine, but numeric_limits<float>::min acutally returns the lowest representable positive float (this is to help calculate another issue with IEEE floats see: http://en.wikipedia.org/wiki/Denormal).
Boost provides a clean solution for this in their “bounds” package.
I’m tempted to just go “-numeric_limits<float>::max()” (that is pretty much what the boost code will compile down to) but I like consistency, and I don’t want to worry about any (if any exist?) unmatched upper and lower values like 32 bit signed ints have, I’m no float expert…
I’ve switch jobs and im working with C++, Objective-C++ and alot of cross platform stuff. Which is a nice change of pace from Java. I’ve extracted out the C++ unit testing framework from one of our IP projects and am actively using it to write unit test for a data exporter im working with, which brings me to my point for this post. And thanks to Kevin who helped me figure this out.
In C++, it turns out function-call arguments are not guaranteed by the standard to be evaluated in the traditional “left-to-right” order. Instead, it’s totaly implementation defined, and I’ve found that on VC++ (2k5) its as you would expect about 90% of the time, but there are certain situations where the compiler decides to mix things up.
the problem was noted in this piece of code:
Vec3 boundsMin( reader.readF32(), reader.readF32(), reader.readF32() )
the compiler translated it into something along these lines:
call readF32 /* stores the result in ecx */
push ecx
fstp dword ptr [esp]
call readF32
push ecx
fstp dword ptr [esp]
call readF32
push ecx
fstp dword ptr [esp]
lea [boundsMin]
call Vec3::Vec3
notice how the store instruction “fstp” always writes to the memory location specified by esp? now the assembly generated for Vec3 constructor’s uses the “rep stos” instruction to “block copy” the contents of the location esp refers to. Initially, all this code looks like it happens how you would expect: “left-to-right” but the reason it doesnt work as expected is because the “push” instruction is decrementing the value in esp by 4 and writing the value of the operand to the top of the stack every time its called.
What does this mean? in simple terms, yes, the reads happen in the order A,B,C but the resulting values are written into the stack-frame effectively backwards C,B,A (I say effectively because the “rep stos” instruction reads from lower memory to higher memory, but C was written to lower memory (because it was written last, hence esp would be at its lowest value), B in higher memory after C and A in even higher memory after B). This is where the non-guaratee by the standard comes into play, the only reason the compiler does this is because there is no requirement to do otherwise. hence all the values for this particular vector were having their x and z values swapped consistently in “Debug” generated code.
Fun times.
Oh and the lesson from all this: don’t inline function argument-expressions which have dependent reads. e.g. reading from streams.
For more reading: http://www.embedded.com/story/OEG20020429S0037
and: http://compilers.iecc.com/comparch/article/95-07-089
I haven’t blogged in a while now, so I thought I’d write something about a new personal project I’ve started. I entitled the post “relearning C++” because though I know lots of the language quirks and the nitty-gritty of C++, I have considered the past year or so a total re-introduction in the way I apply C++ and in particular, go about developing software using C++. For 10 years now I have used C and C++ in my daily life, being self taught from an early age it has lead to lots of painful (to say the least) learning experiences and I would have to say that up until recently my C++ code has had that “self taught” feeling to it – Most of you know what I mean, mediocre variable names, slightly odd coding styles, choosing brevity over clarity and so forth.
The last few years working with Java in the industry has given me a little bit of insight on producing quality software over software that just does what it’s supposed to, this is the “relearning” I refer to.
The new project has been written in a unit-test centric manner, that’s not to say its TDD nor has it 99% code coverage, but unit-test are part of the center stage. Additionally, we are making good (not over-the-top) use of the collection of powerful Boost libraries, and using these meta-programming/template heavy libraries has definitely learned me a thing or two about C++ – I guess that’s the thing about C++, no matter how long you’ve been using it effectively, there’s just so much to know and there are always those “ahhh-haa [forgot about that!!!]” moments and I guess for me this makes things a bit more challenging, but a little bit more fun (but probably not as productive as it could be).
I wrote the unit testing framework for our project, quite a simple suite/runner system. The system has been valuable for us as a 2-man-decentralised-after-hours-team (2MDAHT) – basically self documentation for code and use-cases and full regression testability, which when communication is not as often as we’d like it, is a great way to make sure we’re always taking two steps forward and none back.
The system is also written thread-safe from the ground up, the unit tests and our design-test-implement-test philosophy allow us to see deadlock situations during the design-test phase and then during implementation allow us to more easily replicate and fix deadlocks by drastically narrowing the window of code which could be causing the problem.
If you had asked me 2-3 years ago whether unit-testing C++ code was worth it, I would’ve almost certainly would have said “no” or at best “it depends”, today I would go so far as to say it would be a “must” (however I do appreciate the feeling of impending doom when faced with retro-fitting legacy C++ for unit testing).
After a long absence, I’ve brought back WEVE (Windows Environment Variable Editor). I use it a lot, and quite a few people got some good use from Weve 1.5. This came about when today I got an email from Robert Hadjiyski asking what ever happened to WEVE, and if I would be able to provide the source code so that he may make bug fixes/improvements.
This reminded me that I had Weve 2.0 sitting around on my HDD gathering dust. So I’ve put the binaries up in a ZIP file, and I have applied to have a source forge page created to host/manage the project’s source code. At some stage I’ll update the MSI installer project and stick that version up too.
Hope people like the 2.0 improvements, alot of handy features that make WEVE 2.0 even easier and more intuitive to use than 1.5.
Get it from the WEVE page
Recently at work, I’ve been using XSLT transforms in our backend quite a bit. But the issues I’ve run into took stupid amounts of effort to resolve and it wasnt easy to find any kind of list of do’s and dont’s – so I’m gonna make one from the stuff I’ve just discovered!
- When dealing with DOMSource and DOMResult – always setNamespaceAwareness(true) on your DocumentBuilder/DocumentBuilderFactory !!! – otherwise you end up with empty Documents and no indicators as to why!
- Remember that an XSLT transformation is not guaranteed to return an XML document! Hence, Don’t automatically assume a DOMResult( docBuilder.newDocument() ) will return a valid object – if your XSLT doesn’t produce valid XML, you’ll get an empty DOMResult document node!
- Validation settings affect whitespace awareness – if you don’t have validation on, whitespace is NOT ignored, so make sure your XSLT is formatted correctly – the most common problem area is when you try to prettyfy your .xsl file using indentation etc. Remember that XSL is pretty much an in-place tempate file [Copy Contents -> Replace Stuff exactly where it's asked for -> Dump result!]
Lastly, and easy way to transform input XML and pretty print it.
TransformerFactory transfac = TransformerFactory.newInstance(); Transformer trans = transfac.newTransformer(); trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); trans.setOutputProperty(OutputKeys.INDENT, "yes"); Document doc = buildXml(); /* get your XML document from somewhere.. */ StringWriter stringWriter = new StringWriter(); StreamResult result = new StreamResult(stringWriter); DOMSource source = new DOMSource(doc); trans.transform(source, result); System.out.println(stringWriter.toString());
Recently my engine has needed an upgrade to its event system (observer/publisher). Currently, there is little overhead involved but this is achieved by using macro’s which inline function calls. This is neither maintainable nor extensible and has lead to problems while trying to develop a reliable shutdown sequence for the engine (e.g. when fatal exceptions are caught) – this is mainly due to the intense parallelism present in the architecture.
I found several implementations of the “signals & slots” design pattern, the stand-outs were boost::signals, sigc++-2.0 and a very lightweight implementation called sigslot-1-0-0.
Reading on the internet (boost mailing lists as well as Sneftel’s post on GDnet) lead me to believe that boost::signals had performance issues, however with the recent release of 1.36.0 I wasn’t sure about how valid these bits of information were, so I spent a few hours today developing a benchmark which netted the results inserted at the end of this post.
The signals_bench.cpp C++ source code is not as simple as it could be, but should be easy enough to follow. I chose to use templates/compile time code generation to keep runtime overhead low and also as a bit of practice, as my full-time job is uses Java.
So Boost seems to be faster when you have many signals and many slots for each signal. The 1000×1000 test returned 22 seconds for sigc++ and only 3 seconds for boost::signals. Don’t really know why, but it’s a bit out of place when you take note that boost signals basically gets its ass handed to it by sigc++ in all the other tests. the 5×100000 test b::s returns 1.5 seconds where sc++ returns 0.5 seconds.
The next thing to test is multi-threaded performance, neither library natively supports threading, however sigc++ has an addon called sigc++extras or sigcx, interestingly sigslots is already thread safe. I will have to manually wrap boost::signals so that will be interesting to do..
I will be including sigslots in the next round of testing… whenever that is. (I ran out of time today, might repost with those results if I have time this week)
Just on a side note, the two libraries syntaxes are so similar, you may be able to develop a hybrid wrapper which can switch libraries at runtime. Would be interesting to see how this would help if it were used in a “real-world™” scenario.
Results
| description | setup time | run time | signals | slots | |
|---|---|---|---|---|---|
| boost::signals 1.36.0 | 0.003000 | 2.988000 | 1000 | 1000 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 100 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.003000 | 100 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.029000 | 100 | 100 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 5 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 5 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 5 | 100 | |
| boost::signals 1.36.0 | 0.001000 | 0.015000 | 5 | 1000 | |
| boost::signals 1.36.0 | 0.001000 | 0.148000 | 5 | 10000 | |
| boost::signals 1.36.0 | 0.010000 | 1.487000 | 5 | 100000 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 4 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 4 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 4 | 100 | |
| boost::signals 1.36.0 | 0.001000 | 0.012000 | 4 | 1000 | |
| boost::signals 1.36.0 | 0.001000 | 0.119000 | 4 | 10000 | |
| boost::signals 1.36.0 | 0.011000 | 1.189000 | 4 | 100000 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 3 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 3 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 3 | 100 | |
| boost::signals 1.36.0 | 0.000000 | 0.008000 | 3 | 1000 | |
| boost::signals 1.36.0 | 0.001000 | 0.090000 | 3 | 10000 | |
| boost::signals 1.36.0 | 0.011000 | 0.895000 | 3 | 100000 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 2 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 2 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 2 | 100 | |
| boost::signals 1.36.0 | 0.000000 | 0.006000 | 2 | 1000 | |
| boost::signals 1.36.0 | 0.001000 | 0.059000 | 2 | 10000 | |
| boost::signals 1.36.0 | 0.011000 | 0.585000 | 2 | 100000 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 1 | 1 | |
| boost::signals 1.36.0 | 0.000000 | 0.000000 | 1 | 10 | |
| boost::signals 1.36.0 | 0.000000 | 0.001000 | 1 | 100 | |
| boost::signals 1.36.0 | 0.000000 | 0.003000 | 1 | 1000 | |
| boost::signals 1.36.0 | 0.001000 | 0.029000 | 1 | 10000 | |
| boost::signals 1.36.0 | 0.011000 | 0.291000 | 1 | 100000 | |
| sigc++ 2.0.18 | 0.000000 | 21.526000 | 1000 | 1000 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 100 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.001000 | 100 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.012000 | 100 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 5 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 5 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.001000 | 5 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.005000 | 5 | 1000 | |
| sigc++ 2.0.18 | 0.000000 | 0.046000 | 5 | 10000 | |
| sigc++ 2.0.18 | 0.000000 | 0.473000 | 5 | 100000 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 4 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 4 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 4 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.004000 | 4 | 1000 | |
| sigc++ 2.0.18 | 0.001000 | 0.037000 | 4 | 10000 | |
| sigc++ 2.0.18 | 0.000000 | 0.378000 | 4 | 100000 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 3 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 3 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.001000 | 3 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.002000 | 3 | 1000 | |
| sigc++ 2.0.18 | 0.000000 | 0.029000 | 3 | 10000 | |
| sigc++ 2.0.18 | 0.001000 | 0.287000 | 3 | 100000 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 2 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 2 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 2 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.002000 | 2 | 1000 | |
| sigc++ 2.0.18 | 0.000000 | 0.020000 | 2 | 10000 | |
| sigc++ 2.0.18 | 0.001000 | 0.196000 | 2 | 100000 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 1 | 1 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 1 | 10 | |
| sigc++ 2.0.18 | 0.000000 | 0.000000 | 1 | 100 | |
| sigc++ 2.0.18 | 0.000000 | 0.001000 | 1 | 1000 | |
| sigc++ 2.0.18 | 0.000000 | 0.010000 | 1 | 10000 | |
| sigc++ 2.0.18 | 0.001000 | 0.110000 | 1 | 100000 |
Just because for a standard install, its really simple, but you have to read the getting started guide which has alot of info, but it’s not necessarily “to the point”, if you know what I mean.
- Download Boost 1.36.0 and Bjam (http://www.boost.org/users/download)
- Add Bjam to the PATH if you haven’t already
- Navigate into where you extracted the boost code, the folder is probably named “boost_1_36_0″… e.g. cd C:\Libraries\boost_1_36_0
- invoke bjam with the following arguments: <configuration> –toolset=msvc <output directory name>. e.g. “bjam debug –toolset=msvc stage” builds the entire boost library, and spits out debug .dll’s and .lib’s into “C:\Libraries\boost_1_36_0\stage\libs”
- Add/update these paths in your IDE or your LIB and INCLUDE environment variables e.g. in MSVC I updated my VC include directory list to have “C:\Libraries\boost_1_36_0″ and my VC lib directory list to have “C:\Libraries\boost_1_36_0\stage\libs”
- in your C++ #include <boost/MODULE.hpp> and you should be good to go! (with MSVC the appropriate lib is automatically linked against, if you’re directories are all good, you shouldn’t have to modify your individual project settings)
- Write awesome C++ code!!
