Friday, September 19, 2008

Qt License Restructuring (Yet Another Price Increase)

Between a bad cold and project commitments, I haven't had much time to spare for my blog.  I'm almost caught up and have turned my attention to evaluating Ultimate++, WxWidgets, and Qt for my mISV project. 

Today, I received a phone call from a representative at TrollTech/Nokia about my inquiry on price of a dual platform Qt license.  TrollTech no longer has pricing information on their website -- Qt is now expensive enough that you have to submit a form and a sales person will call you back. 

This is, a recipe for disaster.  I abhor talking to professional sales people, because I don't like to be convinced or pressured into purchasing something before my nerdy analytical mind says it is a good idea.  But, I digress...

For those of you who aren't familiar with Qt, Qt is a C++ framework much like MFC, with a twist.  Since the API/classes have been implemented on the different operating systems, you build your applications using the Qt classes and simply recompile your application and it will run on Windows, Mac OS X, Linux/X11, and now Windows CE. 

In all honesty, it is a very good framework and you can knock out a very good application in a relatively short period of time if you invest the time and effort to learn the class architecture.

Further, if you write your code under the GPL license, you can use the Qt platform for free.  However, if you wish to write a commercial application, you have to fork out a considerable amount of cash to do so legitimately.

The sales representative informed me that the licensing structure has changed.  I scribbled down the new pricing:

Old Price Oct 1. Price Platform (per developer)
$3,300 $3,700 single platform
$4,950 N/A dual platforms
$6,600 $5,300 all three platforms

The simplified version is that if you pull the trigger and purchase the dual platform license, you get the third (Linux) for free starting October 1st.  Delay and your cost will rocket to $5,300 after October 1st.

Or to put this more succinctly: after October 1st, you will not be able to purchase just the dual platform option (Mac OS X and Windows).  You will be forced to purchase the 3 platform license for more money.  You are forced to take Linux platform, even if you don't want it.

To bring it home with a very practical example.  Let's say that I am developing a shrink wrapped retail product for sell, with a sale price of $29.99.  Assuming I purchase a Qt dual platform license to develop this product, I'm out $4,950.  Given the price point of $29.99 (and disregarding hosting, bandwidth, transaction fees, etc.), I would have to sell 166 copies just break even. 

Additionally, if I expand later and hire employees (or contractors) the licensing issue gets murky.  The license is bundled to a developer (or more specifically, their email address).  You can change the developer/email address attached to a license once a year.

Besides price, the only other criticism I have is that, as I found out, the framework is fluid.  The graphics/canvas classes have changed in Qt4.5 and a portion of the Qt4 book seems to be obsolete. 

Additionally, given the uncertainty of Nokia's future stewardship of Qt and the mercenary price increases, I have to wonder if getting locked into a vendor's platform is a good idea going forward. 

Labels: ,

Friday, April 25, 2008

Wt: C++ Framework Evaluation

I blogged a while back that I was evaluating various web platforms for a my projects.  Well, tonight I began to seriously evaluate the Wt (pronounced witty) C++/Ajax framework.  I've decided not to use the library for one simple reason -- the licensing terms are cost prohibitive.

I had already floated Wt to a client as a possible alternative (recovery plan) to a massive Java/JSF project that was nearly a year behind schedule.  The leads were receptive to the idea so next on my agenda was to seriously evaluate it, generate some applications and mock up a demo.

First, let me say that Wt it is an awesome idea, and it is great implementation.  It leverages boost::asio (asio was included in boost with version 1.35).  Out of the box, it works with apache, fastcgi, or an extremely lightweight httpd server. 

In a nutshell -- you write your application by cobbling together widgets much in the same way you would write an application in Qt.  The underlying technologies (HTML/XHTML, JavaScript, CSS, Ajax, Forms, DHTML, etc) are abstracted so you concentrate on adding, say a button widget rather than coding up an HTML form that contains a button, and code to handle the post... You simply add a button widget to your container class and the HTML, JavaScript and forms are generated automatically at runtime.

While the examples are somewhat terse, they do showcase the possibilities -- with some effort in developing your own widgets, you could have the coding efficiency of Ruby On Rails with the speed of C++.

The framework is licensed in a dual licensing scheme.  They freely grant you a free GPL license.  However, the commercial license is € 599 per year, per developer.  At current exchange rates that is nearly $1000 per year, per developer.   

I don't mind paying for a license, and I often encourage my clients to purchase products like RedHat or MySQL.  That being said, I simply don't like purchasing licenses that expire, where you are locked in. 

What happens when you develop a 1MLOC application and the dollar continues to fall in relation to the Euro, and they want to raise their prices you are effectively a hostage.

What do you do?  Rewrite/port the application at a tremendous cost?  I've seen companies held hostage by framework providers before and really don't want to be in the position. 

Labels: , ,

Thursday, March 27, 2008

LD_LIBRARY_PATH in Mac OS X

I keep forgetting, so I thought I would drop a quick blog posting to remind me forever.  For those of you who are moving from Linux to Mac OS, you might be interested:

The equivalent of $LD_LIBRARY_PATH in Linux, is (drum roll please): DYLD_LIBRARY_PATH.

$ export DYLD_LIBRARY_PATH=`pwd`/mydebugpath

And now your application will resolve the shared objects (.dynlib) files at runtime.

For all the glory details you can also man dyld: 

$man dyld

Labels: , ,

Tuesday, January 29, 2008

Book Review: Writing Solid Code

Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs (Microsoft Programming Series)
by Steve Maguire

Read more about this book...

I finally got around to reading Steve Maguire's Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs, thanks primarily to a coworker who lent it to me to read over the weekend. 

Written in a personal, easy flowing style, it contains many suggestions for improving your coding process, when developing in C.  It is a fast read, and I liked the book even though I felt that much of it was somewhat outdated.

Steve Maguire wrote this book in a different time, a time before OOAD, C++, Java, RUN, XP, Agile, XP, and TDD.  The book grew out of his experiences at Microsoft and contains practical advice and lessons learned about software construction and projects.

He even starts by asking two questions about bugs found in code: 1) How could I have automatically detected this bug? and 2) How could I have prevented this bug?  Therein, he starts to build the process and techniques for eliminating bugs.

The generalized advice is certainly still valid:

  • enable compiler warnings and investigate each one; 
  • use assertions;
  • fix bugs now (don't wait until "feature complete" to go back and fix them);
  • don't quietly ignore error conditions;
  • don't write multi-purpose functions, focus on very specific functions that can be checked;
  • write code for the average programmer (make it readable);
  • don't wait until a bug to step through the code;
  • developers are responsible for finding bugs;
  • porting code is new development and should be tested as such;
  • carefully bound check arguments

Conclusion

The book is an interesting snapshot in time, and contains a lot of good advice.  However, the book is just simply too dated to be recommended.  What was once a ground-breaking collection of cutting edge advice now should be common sense for most experienced developers.

It was recommended as a must read "classic."  It isn't.

Labels: ,

Thursday, October 25, 2007

GNU Internationalization Presentation

Earlier this year I did a brief presentation to a group of C++ developers about internationalization, localization, and the GNU gettext utilities.

Tonight, I uploaded the slide deck to slideshare.  Enjoy.

Labels: , ,

Tuesday, October 2, 2007

C++ Web Development Platform

After some serious consideration I've decided to go with an amalgimated approach to developing my web services architecture.  The pretty marketing stuff will be HTML/CSS/Flash, but the main applications will be written entirely in C++. 

Next, I resolved to leverage the Boost C++ Libraries and FastCGI.  FastCGI will remove the process creation from the invokation of a CGI application making the performance way faster. 

Lastly, I need to decide on the last piece or whether or roll my own. There is a limited number of C++ frameworks that are usable right "out of the box."  Some frameworks I am currently evaluating are:

  • Microsoft ATL Server.   The code is now located at: www.codeplex.com/atlserver.  License:  Microsoft Limited Permissive License (MS-LPL).
  • Leverage Apache/Axis & Apache mod.     
  • Microsoft IIS/IIS filters.
  • GNU Cgic.   GNU cgicc is an ANSI C++ compliant class library that greatly simplifies the creation of CGI applications for the World Wide Web.  This is basically a collection of classes and templates that hand you a parsed CGI environment.
  • WT.  Wt (pronounced wit-ty), is a C++ library to develop AJAX aware web applications, built around a graphical UI coneceptual framework.  This looks to be by far the coolest.  The question is will it stand up to some serious scrutany?
  • Requirements

    Given my previous embedded systems development I should be able to build up a system with a minimum linux install that is capable of saturating a 100MB ethernet connection. 

  • Extreamly reliable and robust.  Given the general lack of employees, I simply cannot afford for customers to call me to tell me that something is broken.  It has to work 24/7 without producing java stack dumps or IIS/.NET SQL/odbc error traces.    

  • Highly dynamic site – large portions of the content cannot be cached effectively and need recreating on every view.

  • Huge number of visits (probably not going to happen, but I can dream).

  •  

     

    Labels: , ,

    Monday, September 17, 2007

    C++: Pragmatically Generating a Back Trace/Stack Dump

    When you work on a very large embedded project with multiple developers on different continents, all dumping code into a project, it becomes very difficult to figure out what is broken and more importantly, who is the best person to go fix it. One way is to generate core dumps and then use gdb and the bt command to print the back trace, but core files can become large and not everyone knows how to analyze a back trace. So invariably, one or two developers end up being called in on every problem where they guestimate where it may be, and assign the bug. Arguments ensue and time is wasted. It can turn into a tremendous waste of time. If things get bad enough, I will add C++ code that will generate a back trace (sometimes referred to as a stack trace) in a human readable format. With that I can quickly say which part of the application broke, and go find the owner to assign the bug to. If you are using Linux/gcc/glibc you can generate a back trace with the backtrace() and backtrace_symbols() function calls. Below is an example:


    #include <stdio.h>
    #include <execinfo.h>

    void backtrace(void)
    {
    void *addresses[10];
    char **strings;

    int size = backtrace(addresses, 10);
    strings = backtrace_symbols(addresses, size);
    printf("Stack frames: %d\n", size);
    for(int i = 0; i < size; i++)
    {
    printf("%d: %X\n", i, (int)addresses[i]);
    printf("%s\n", strings[i]);
    }
    free(strings);
    }

    Labels: ,

    Thursday, September 6, 2007

    How Not to Implement Serialization in C++

    I spent most of the day knocking out a nice PowerPoint slide deck to walk a customer's developers through a large swath of code I just checked into their subversion repository.  I hammered out a framework that would improve productivity and implementation.  With a littel luck, the project would be back on schedule. 
     
    The framework included serialization, complements of the Boost serialization library. However, the principle architect balked at using Boost serialization -- I should have used the persistence mechanism that was coded by their developers.  I argued, but when the person who signs my timesheets agreed with the architect, the battle was over. The customer is always right. 
     
    With that, I needed to revamp my code to use the persistence mechanism (which used blocks of memory that were flushed to disk or flash memory), but make it more usable.  Hundreds of lines of, "if (p) p->write(sizeof(x), &x, 1)" are not acceptable to me.  There has to be a better, more developer friendly way. 
     
    I immediately turned to Google for help.  Surely a developer out there had a simular interest in rolling their own serialization scheme.  Amazingly, I found several "tutorials" on serialization that had the exact same theme.
     
    The number one serialization tutorial was the "<ahref="http://www.functionx.com/cpp/articles/serialization.htm" functionx C++ Object Serialization tutorial</a>. It should be entitled, "How NOT to Implement Object Serialization." 
     
    Take the following example:
    #include <fstream>
    #include <iostream>
    using namespace std;

    class Student
    {
    public:
        char   FullName[40];
        char   CompleteAddress[120];
        char   Gender;
        double Age;
        bool   LivesInASingleParentHome;
    };

    int main()
    {
     Student one;

      strcpy(one.FullName, "Ernestine Waller");
      strcpy(one.CompleteAddress, "824 Larson Drv, Silver Spring, MD 20910");
      one.Gender = 'F';
      one.Age = 16.50;
      one.LivesInASingleParentHome = true;

      ofstream ofs("fifthgrade.ros", ios::binary);
      ofs.write((char *)&one, sizeof(one));

      return 0;
    }
    So what is wrong with doing this?  It is an extremely poor solution that will only work on the same compiler/platform reliably.
     
    Also, the pointer to the class one doesn't necessarily point to the first data item, public or private within this class.  I spent several days tracking down and squashing a bug in an embedded system because of assumptions like that.  In my case, the original developer cast the class to a char *, then wrote the class to a socket.  Unfortunately for the developer, who assumed that the pointer to the class would point to the private data, he did not consider that this might be compiler dependent.  After looking at network traces I quickly figured out the problem.  One compiler was injecting some extra bytes, causing problems.
     
    Next, what happens when you save a class that contains pointers to other classes?  Kaboom.
     
     

    Labels: ,

    Free Dr. Dobbs Journal and MSDN Magazines

    Developers can register to subscribe to digital editions of Microsoft MSDN Magazine and Dr. Dobb's Journal for free. Simply login HERE using your Windows Live ID (formerly called passport) account and subscribe.
    • A sample of Dr. Dobb's Magazine can be found HERE

    • A Sample of MSDN Magazine can be found HERE.



    Powered by ScribeFire.

    Labels: ,

    Wednesday, September 5, 2007

    Dealing with C++ "Unused Parameter" Warnings

    Generally, when I am working with clients I try to get everyone on the team to agree to a "no compiler warnings" policy and immediately turn on the "warnings as errors" switch. Thereafter, any compiler warning is automatically treated like an error and the build will fail. Unfortunately, when stepping into a project where most of the code was written long ago, there may be hundreds if not thousands of compiler warnings. The only solution is to investigate and fix (or suppress) each one. So how do you suppress the unused parameter warnings when you legitimately don't use the argument? There are three ways: 1. #pragma unused. #pragmas are compiler specific and should be avoided. However, if your compiler supports it, you can use it as follows:

    void my_function(int32 foo)
    {
    #pragma unused foo;
    }

    2. Comment the Argument. Additionally, you can comment out the argument. The compiler will not give you an unused warning:

    void myfunction(int /* arg */)
    {
    }

    3. Cast to void. Casting an unused variable to void will always
    stop the warning.

    #define UNUSED_ARGUMENT(x) (void)x
    void myfunction(int arg )
    {
    UNUSED_ARGUMENT(arg);
    }

    Labels: ,