Tagged: C++ Toggle Comment Threads | Keyboard Shortcuts

  • Andre Pareis 17:14 on 09.05.2012 Permalink | Reply
    Tags: C++,   

    NSInvocation in Objective-C++ 

    UPDATE: This article was initially about some difficulties that I had calling some Objective-C++ code from C++ in a dynamic way using NSInvocation, which failed in the first place. I switched to directly calling the IMP pointer instead while passing C++ object pointers as arguments. Thanks to the comments that I received from bob, an obviously very experienced Objective-C++ developer, I could change the code and make it work also with NSInvocation.

    Here is a C++ class that represents the details of a notifier/observer association called Observation:

        class Observation: public Object {
        public:
    
            void setObjcObserver(NSObject *theObserver) { _nsObserver = theObserver; }
            void setObjcSlot(SEL theSlot) { _nsSlot = theSlot; }
                    
            void notify(bool complete);
            
            Observation() : _nsObserver(nil), _nsSlot(nil) {}
            
        private:
    
            Object *_notifier;
            NSObject *_nsObserver;
            SEL _nsSlot;
        };
    

    First Attempt Using NSInvocation Failed

        void Observation::notify(bool complete) {
    
            NSMethodSignature *aSignature = [[_nsObserver class]
                            instanceMethodSignatureForSelector:_nsSlot];
    
            assert(aSignature);
            assert([_nsObserver respondsToSelector:_nsSlot]);
    
            NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
            [anInvocation setSelector:_nsSlot];
            [anInvocation setArgument:this atIndex:2];
            [anInvocation setArgument:&complete atIndex:3];
                
            [anInvocation invokeWithTarget:_nsObserver];
        }
    

    this code compiled fine but it crashed as soon as I tried to access members of the received Observation object in Objective-C.

    IMP Based Solution

    My approach to solve the issue is that I am directly calling the IMP of the method in the observer object. For this, I slightly change the C++ Observation model to cache the IMP pointer:

        class Observation: public Object {
        public:
    
            void setObjcObserver(NSObject *theObserver) { _nsObserver = theObserver; }
            void setObjcSlot(SEL theSlot) { _nsSlot = theSlot; }
                    
            void notify(bool complete);
            
            Observation() : _nsObserver(nil), _nsSlot(nil), _nsSlotImpl(nil) {}
            
        private:
    
            Object *_notifier;
            NSObject *_nsObserver;
            SEL _nsSlot;
    
            typedef void (*slotImpl_t)(__strong id, SEL, lang::Observation *, BOOL);
            
            // used to cache the IMP of the objcSlot
            slotImpl_t _nsSlotImpl;
    
        };
    

    Here, I simply add a typedef of the desired observer method signature called “slotImpl_t” and a member to cache the IMP function pointer. The cache would not be necessary but in my case the observer IMP will not change, so I cache it in order to save the look-up on frequent execution of the code. The notify() method now looks different:

        void Observation::notify(bool complete) {
            if(!_nsSlotImpl) {
                _nsSlotImpl = (slotImpl_t)[_nsObserver methodForSelector:_nsSlot];
                assert(_nsSlotImpl);
            }
            _nsSlotImpl(_nsObserver, _nsSlot, this, complete);
        }
    

    This is working like a charm now. The address of the Observation object at the receiving end is correct as it should be, even in case of multiple-inheritance combined with virtual inheritance.

    Correct Solution with NSInvocation

    It turned out that in my original solution using NSInvocation I did it wrong. NSInvocation needs the address of the this pointer and not the this pointer itself. To make it worse, when simply doing

            [anInvocation setArgument:&this atIndex:2];
    

    the compiler complains with the error:

    Address expression must be an lvalue or a function designator
    

    So, instead of &this it has to be an lvalue which essentially requires to copy the this pointer to a temporary variable, ‘temp’ in this case. Here is the complete and working code:

        void Observation::notify(bool complete) {
    
            NSMethodSignature *aSignature = [[_nsObserver class]
                            instanceMethodSignatureForSelector:_nsSlot];
    
            assert(aSignature);
            assert([_nsObserver respondsToSelector:_nsSlot]);
    
            NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
            [anInvocation setSelector:_nsSlot];
            Observation *temp = this;
            [anInvocation setArgument:&temp atIndex:2];
            [anInvocation setArgument:&complete atIndex:3];
                
            [anInvocation invokeWithTarget:_nsObserver];
        }
    

    Conclusion

    There is expert knowlegde about Objective-C++ available on the planet. But it’s not easy to get access to it. Sometimes, it helps to blog about a problem and the solution will come and find you;) Again, bob, thank you very much

     
    • bob 21:32 on 04.06.2012 Permalink | Reply

      You are doing it wrong.
      It needs to be

      [anInvocation setArgument:&this atIndex:2];

      • Andre Pareis 21:52 on 04.06.2012 Permalink | Reply

        This one gives me an error: Address expression must be an lvalue or a function designator

    • bob 21:39 on 04.06.2012 Permalink | Reply

      Or if it is not possible to write “&this”, you can do

      Observation *temp = this;
      [anInvocation setArgument:&temp atIndex:2];

      • Andre Pareis 21:58 on 04.06.2012 Permalink | Reply

        I’m pretty sure that I tried “&this” before but, because of the compiler error above, I was mislead to think that taking the address of the this pointer was the wrong idea. Turns out it wasn’t. Using your modification the code is working as expected. Thank you very much for your fix, bob, I will adjust the blog post accordingly in order to not blame the language nor NSInvocation but just my stupidity;)

  • Andre Pareis 10:24 on 31.03.2012 Permalink | Reply
    Tags: C++, ,   

    Java Like Programming in C++ 

    C++ is a nice and very flexible language, but this comes at the cost that it forces you to think about many programming details before you can even think about solving your actual problem. Examples would be:

    • object oriented or generic programming
    • when to use references, value types and pointers
    • memory management rules
    • casting rules, const correctness, virtual methods
    • STL? boost?

    This list can become quite endless. Being in the same boat as every other C++ programmer but having grasped some of the look&feel of other languages like Java and Objective-C (in its Cocoa incarnation) or even Qt (as a good C++ OO style example), I am continuously and unconsciously thinking about the pros and cons of each of them and recently I thought: wouldn’t it be nice to be able to program in C++ but make it look like Java?

    Sure it would! Even it it were only just for fun…

    So, I came up with a working code sample which looks quite like Java but is actually C++. I show you the code sample first before I elaborate on any details. First come some classes which you can consider the “framework”:

    #include <iostream>
    #include <sstream>
    #include <assert.h>
    #include <vector>
    
    
    size_t globalRefCount = 0;
    
    class Object;
    class String;
    std::ostream &operator<<(std::ostream&, Object&);
    std::ostream &operator<<(std::ostream &, const String&);
    
    
    
    class Object {    
    protected:
        
        struct Impl {
            size_t _refCount;
            Impl() : _refCount(0) {}
            virtual ~Impl() {}
        } *data;
        
        inline void retain() {
            if(data) {
                data->_refCount++;
                globalRefCount++;
            }
        }
        inline void release() {
            if(data) {
                data->_refCount--;
                globalRefCount--;
                if(data->_refCount == 0) {
                    delete data;
                    data = 0;
                }
            }
        }
        
        Object(): data(new Impl) {
            retain();
        }
        Object(Impl *imp) : data(imp) {
            retain();
        }
        Object(const Object& other): data(0) {
            operator=(other);
        }
        
    public:
        virtual ~Object() {
            release();
        }
        virtual const char *type() const { return "Object"; }
        
        void operator=(const Object& other) {
            if(data!=other.data) {
                release();
                data = other.data;
                retain();
            }
        }
        
        // make a heap clone of this object for usage in containers
        virtual Object *clone() const {
            return new Object(*this);
        }
        
        virtual String toString() const;
            
    };
    
    
    
    
    class String : public Object {
    protected:
        struct Impl: public Object::Impl {
            std::string str;
        };
    public:
        String(): Object(new Impl) {}
        String(const char *s): Object(new Impl) {
            static_cast<Impl*>(data)->str = s;
        }
        String(const String &other): Object(other) {}
        String operator+(const String& s) const {
            String result;
            static_cast<Impl*>(result.data)->str = static_cast<Impl*>(data)->str;
            static_cast<Impl*>(result.data)->str += static_cast<Impl*>(s.data)->str;
            return result;
        }
        String operator+(const Object& s) const {
            String result;
            static_cast<Impl*>(result.data)->str = static_cast<Impl*>(data)->str;
            static_cast<Impl*>(result.data)->str += static_cast<Impl*>(s.toString().data)->str;
            return result;
        }
        String operator+(long l) const {
            std::ostringstream oss;
            oss << static_cast<Impl*>(data)->str << l;
            String result;
            static_cast<Impl*>(result.data)->str = oss.str();
            return result;
        }
        const char *c_str() const {
            return static_cast<Impl*>(data)->str.c_str();
        }
        bool operator==(const String &other) const {
            return static_cast<Impl*>(data)->str == static_cast<Impl*>(other.data)->str;
        }
        
        // must have's
        const char *type() const { return "String"; }
        Object *clone() const { return new String(*this); }
        
        String toString() const {
            return *this;
        }
    };
    
    std::ostream &operator<<(std::ostream &os, const String& s) {
        os << s.c_str();
        return os;
    }
    
    String Object::toString() const {
        std::ostringstream os;
        os << this->type() << "@" << (void *)this << "[" << (data ? data->_refCount : 0) << "]";
        return String(os.str().c_str());
    }
    
    std::ostream &operator<<(std::ostream &os, Object& o) {
        os << o.toString().c_str();
        return os;
    }
    
    
    
    
    class ClassCastException: public Object {
        struct Impl: public Object::Impl {
            String message;
        };
    public:
        ClassCastException() : Object(new Impl) {}
        ClassCastException(const String& msg) : Object(new Impl) {
            static_cast<Impl*>(data)->message = msg;
        }
        const char *type() const { return "ClassCastException"; }
        Object *clone() const { return new ClassCastException(*this); }
        String message() const {
            return static_cast<Impl*>(data)->message;
        }
        String toString() const {
            return message();
        }
    
    };
    
    
    
    class ArrayList: public Object {
        struct Impl: public Object::Impl {
            std::vector<Object*> _data;
        };
        
    public:
        ArrayList(): Object(new Impl) {}
        ~ArrayList() {
            Impl *self = static_cast<Impl*>(data);
            for (std::vector<Object*>::iterator it = self->_data.begin(); it!=self->_data.end(); it++) {
                delete *it;
            }
        }
        void add(const Object& element) {
            static_cast<Impl*>(data)->_data.push_back(element.clone());
        }
        size_t size() const {
            return static_cast<Impl*>(data)->_data.size();
        }
        
        Object &at(size_t index) const {
            return *static_cast<Impl*>(data)->_data.at(index);
        }
        template<class T> const T &at(size_t index) const {
            Object *o = static_cast<Impl*>(data)->_data.at(index);
            T *t = dynamic_cast<T*>(o);
            if(t) return *t;
            throw ClassCastException(o->type());
        }
        
        const char *type() const { return "ArrayList"; }
        Object *clone() const { return new ArrayList(*this); }
    
        String toString() const {
            std::ostringstream oss;
            oss << Object::toString() << "(";
            for (size_t i = 0; i<size(); i++) {
                oss << at(i).toString();
                if(i+1<size()) {
                    oss << ",";
                }
            }
            oss << ")";
            return oss.str().c_str();
        }
    
    };
    
    
    class OutputStream: public Object {
        struct Impl: public Object::Impl {
            std::ostream &stream;
            Impl(std::ostream &os): stream(os) {}
        };
        OutputStream() {}
    public:
        
        OutputStream(std::ostream& os): Object(new Impl(os)) {}
        OutputStream(const OutputStream &other): Object(other) {}
    
        void println(const Object &object) {
            static_cast<Impl*>(data)->stream << object.toString() << std::endl;
        }
        
        const char *type() const { return "OutputStream"; }
        Object *clone() const { return new OutputStream(*this); }
    
    };
    
    

    Now let’s see how to use it in client code. I supply only a main() here but I have some anonymous blocks to show the effects of scoping:

    
    struct system {
        OutputStream out;
        system(): out(std::cout) {}
    };
    
    struct system System;
    
    
    int main (int argc, const char * argv[]) {
        
        assert(globalRefCount==1); // 1 is for System.out
        
        {
            String s;
            assert(globalRefCount==2);
        }
        assert(globalRefCount==1);
        
        
        {
            String s = "Connecting...";
            System.out.println(String("s = ") + s);
            
            String dots = "the dots";
            String t = s + " " + dots + ".";
            System.out.println(String("t = ") + t);
            
            ArrayList l;
            l.add(s);
            assert(l.size() == 1);
            assert(globalRefCount==6);
            
            l.add(t);
            l.add(ArrayList());
            System.out.println(String("l = ") + l);
            assert(globalRefCount==8);
            
            try {
                System.out.println(String("l[0] as String = ") + l.at<String>(0)); // ok
                System.out.println(String("l[1] as String = ") + l.at<String>(1)); // ok
                System.out.println(String("l[2] as String = ") + l.at<String>(2)); // this throws!
                assert(false);
            } catch(ClassCastException e) {
                System.out.println(String("ClassCastException: ") + e);
                l.add(e);
            }
            
            
            // adding to the list in other scope will keep the object valid
            {
                String other = "Created in other scope";
                l.add(other);
            }
            System.out.println(String("l[3] as String = ") + l.at(3));
            assert(l.size()==5);
            assert(l.at<String>(4) == "Created in other scope");
            System.out.println(String("l = ") + l);
        }
        assert(globalRefCount == 1);
        
        return 0;
    }
    

    Pretty much like Java, isn’t it?

    I have written this just as a proof of concept. As such, it is fully working and I like it so far. It could serve as a good starting point for a complete implementation. Here’s the output when executed:

    s = Connecting...
    t = Connecting... the dots.
    l = ArrayList@0x7fff5fbff8c0[1](Connecting...,Connecting... the dots.,ArrayList@0x100100b00[1]())
    l[0] as String = Connecting...
    l[1] as String = Connecting... the dots.
    l[2] as String = ClassCastException: ClassCastException@0x100100cb8[1]
    l[3] as String = ClassCastException@0x100100b20[1]
    l = ArrayList@0x7fff5fbff8c0[1](Connecting...,Connecting... the dots.,ArrayList@0x100100b00[1](),ClassCastException@0x100100b20[1],Created in other scope)
    Program ended with exit code: 0
    

    Fundamental Design

    Java is (with the exception of primitive types like int, double etc. and their array forms) an object-oriented language. Everything in Java or Objective-C derives from a common and well known base class. So there is a base class Object in this example as well and there is an equivalent of the platform type String as well as a sample collection called ArrayList which is holding just Objects.

    One thing is very important: There is no need for pointers as memory management is part of the solution! For instance, the ClassCastException that is thrown can be added to the ArrayList without having to worry about leaking memory afterwards. It’s not complete yet though as the notion of “weak” pointers is still missing (think: ARC), but the strong part it is fully working here.

    The whole idea of the implementation is based on the well-known PIMPL idiom but it also throws the idea of the smart_ptr into the mix but it even goes further. Firstly, all behavior and data are strictly separated, not just the private members. Each conceptual class like String, ArrayList or ClassCastException has a functional class that implements behavior only and no data at all, it acts like a fully functional proxy to the data. This makes it possible to clone (copy assign) these proxy objects very cheaply, because they consist only of 2 pointers (data and _vtable). The actual data is implemented in the nested class “Impl”. There is one specialized Impl class for each conceptual class (1v1 mapping). Both the conceptual classes and the Impl classes span two parallel type hierarchies. As one picture says more like 1000 words, here it is:

    In the base Impl (Object::Impl) a smart_ptr like reference counting is implemented (_refCount). I have explicitly added two methods Object::retain() and Object::release() in the code to express the similarity to Objective-C’s NSObject, but this is all handled internally during copy construction or assignment.

    Conclusion

    I have still to decide wether an approach like this is generally feasible. What I like is to be able to clone good concepts and class library designs from other languages like Java or Objective-C into C++ and continue coding without having to worry about the aforementioned detailed C++ design decisions that trouble me every day.

    Of course, I would have to implement ARC style memory management completely before it can be used, otherwise cyclical references would leak. Also, I’d like to mention that I’m fully aware that a coding style like this leads to immediate code bloat. But so does the PIMPL idiom. In order to mitigate that, I have a flexible code generator on my side which let’s me do most part of the actual coding in UML as opposed to hand-crafting it where I would definitely think twice or even more before traveling down this road…

    View the full source: https://gist.github.com/2279561

     
  • Andre Pareis 16:08 on 13.03.2012 Permalink | Reply
    Tags: C++,   

    Multiple Inheritance in Objective-C / Core Data vs C++ 

    Multiple inheritance is hard. In fact it is so hard, that only very few programming languages support it. Objective-C is one for instance, where support for multiple inheritance is limited to the conformance to @protocols. Behavior can only be inherited from one single base class.

    If you need multiple inheritance in Objective-C you have several options to choose from. Most of the time when you are looking for answers to the question of how to do multiple inheritance in Objective-C the right way, you will be pointed into one of two directions: #1 don’t use it because it implies a flawed design. #2 do it using composition (via delegates).

    Option #1 I do not like at all. There are cases where MI is very well suited and only because Objective-C doesn’t support it doesn’t mean it is bad. It just means that the language designers considered it way too complicated to implement for the benefit of a few cases where it makes sense.

    For instance, my current use case with strong need for multiple inheritance support is the UML specification. UML makes heavy use of multiple inheritance and if you study the UML model you will find that the abstractions found in there make very well sense because they eliminate redundancy and the need to explain what’s going on. All those abstractions are basically orthogonal classifications which can be combined in a subclass to express things very precise and in a type-safe manner.

    So, if you are forced to deal with multiple inheritance in your program you can do so with option #2 in Objective-C. However, in my opinion, this has limitations. I will give you an example: Imagine a model like this:

    Let’s say we map this to the following physical implementation in Objective-C. Here, the greenish elements are Objective-C @protocol and the yellowish elements are Objective-C @class:

    It follows the often heard recommendation to map inheritance using composition. Here, the Class part of an AssociationClass is mapped to a delegate called “theClassImpl”, whereas the Association base class is mapped to plain Objective-C inheritance.

    Suppose now we want to map this structure to CoreData. We need to model NSManagedEntity with NSManagedProperty. CoreData does not work on top of @protocols but on actual @classes. Therefore, we have one physical implementation of the association between Class and Property (owningClass-properties).

    But here comes the big BUT: This can only work if we have full control over the OR mapping! CoreData on the other hand, does not rely on interfaces but on the actual implementations. That means, we must publish the otherwise internal composition mapping of theClassImpl to CoreData. If we then have a client of Class (for instance: Property::owningClass) then it will not be possible to downcast such a Class obtained from the persistence layer into an AssociationClass. Instead, it would be necessary to navigate backwards from the Class to the actual AssociationClass. But this kind of “alternative” cast can not be implemented transparently using Objective-C language constructs. An [aClass isKindOfClass:[AssociationClassImpl class]] would yield a technical “NO” and it’s not possible to extend the language to make it yield “YES”.

    Such an MI -> SI mapping scenario can only work if every consumer solely relies on the Interfaces only and makes no assumption about internal structures. This would imply that the ORM uses factories instead of instantiating from its meta information. In CoreData, this is not the case.

    This is why you can pretty much ignore the advice how to map multiple inheritance at the language level if you don’t also consider the APIs you’re dealing with because those APIs will render the easy sounding solution in the context of reality useless pretty quickly. In my case, I was forced to implement the model part of the system in C++ because C++ has awesomely good support for multiple inheritance. All problems related to the mapping of multiple inheritance to the microprocessor architecture had been solved by Bjarne Stroustrup in C++ since day 1. Read here why and how: http://drdobbs.com/184402074

    Here’s how the dreaded diamond from the example above would be implemented in C++:

    class Element {
    };
    
    class Association: public virtual Element {
    };
    
    class Class: public virtual Element {
    public:
        std::vector<class Property*> properties;
    };
    
    class Property: public Element {
    public:
        Class *owningClass;
    };
    
    class AssociationClass: public Association, public Class {
    };
    

    A straight 1:1 mapping of the concept to the language. Here, class “AssociationClass” would fully inherit the behavior of Class::properties without the need to implement something special. It just works. But, in comparison to Objective-C the C++ implementation lacks support for Core Data. But: so does multiple inheritance in Objective-C with CoreData! So no real difference here.

    Conclusion

    Multiple inheritance with CoreData is close to impossible except for very simple cases. With C++, besides all its ugliness and controversy, at least you get multiple inheritance in the language and usually in an implementation quality without the need to waste your time thinking around the whole concept.

     
  • Andre Pareis 18:15 on 22.04.2010 Permalink | Reply
    Tags: C++, ,   

    Why I completely dropped Qt and QtWebKit from my Hybrid App 

    In my recent articles I described certain aspects related to the use of QtWebKit in a hybrid C++ application. In the meantime, it turned out that Qt and QtWebKit are not such a good bet as I thought they would be at the beginning. Here’s why:

    Apple

    First and foremost, the most interesting question in a hybrid app written in C++ is: where do you want it to run? In my case, it is on Mac OS X in the first place, then on Windows and very likely also on Apple’s iPhone or iPad. However, this puts me in a situation where I have to decide which subset of these platforms I support primarily and which ones secondarily as there is no such thing as a common layer to access all of them from a single code base. It is either a combination of Mac OS X and Windows via the Qt port or a combination of Mac OS X and iPad/iPhone that can go as the primary target.

    For a certain time, I concentrated on the first combination as shown above while hoping that time works for me and somebody ports Qt to the iPhone, and there already is a project for that at http://www.qt-iphone.com/Introduction.html. But, in the meantime, Apple introduced iPhone OS 4.0 and *bam* there will never be such a thing, because http://daringfireball.net/2010/04/iphone_agreement_bans_flash_compiler “and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited)”, a rule introduced by Apple which is actually targeted against Adobe and its Flash technology but will also affect Qt, especially because that comes from Nokia, another one of Apple’s (future arch) rivals. This means that Qt can never be the common layer on all of my targeted platforms. Given that, and the fact I target Mac OS X first, Windows second, and that I also tend to shift Windows one step further down and promote the iPad version instead which completely changes the porting picture:

    This looks much cleaner now as it provides unified, access to all Apple platforms. For Windows and Android Google Chromium is a good choice.

    But what about Qt in general?

    Qt / Nokia

    The first part was the rational part, here comes the emotional one:

    In the recent weeks I had enough time to dive into the details of Qt in all aspects. Its visual quality, its API quality, its implementation quality and the quality and structure of their development process. My conclusion from that is that it is the wrong way of doing things as evaluated from a broader perspective.

    What is very important in a visual app is visual quality. Qt does a good job in replicating functionality of controls on each addressed platform. But, it can never, by its nature, take care about the special controls certain platforms offer because then it wouldn’t be cross-platform any more. Qt certainly has to focus on the lowest common denominator across all supported platforms. By driving the GUI via Qt controls, an application loses the capability to provide the eye-candy to the platform user like he is used to. The most prominent example of this is Google Maps which is based on Qt and which totally lacks platform L&F on the Mac up to a degree where it certainly loses style. In Google Maps this is not so important as interaction with the Earth requires a special user interface anyway and the rest of the app is unimportant, but it shows how difficult it is even for Google to provide attractive visual quality on the platform. Gaining access to the platform specialities is difficult as everything is hidden by Qt in private implementations. You have no chance to do “that special thing”.

    Qt has a comprehensive “fat” API that consumes everything from all platforms and which has to take care about the specialities on all platforms, which is of course its nature but which is also very difficult to maintain and to improve over years. Development of Qt is distributed across Europe with Nokia focussing on minimizing cost not maximizing quality. Their development centers are mostly in cheaper parts of Europe and they try to cooperate with educational institutions to bring the costs down. This might be a legacy from Trolltech but is still in place and shows that quality is not their first concern.

    So far I have touched Qt releases 4.5, 4.6 and 4.7 and came across older API documentations. From there I can tell that many things they have done are not very well and strategically thought out. They introduced too many APIs doing similar things and deprecating previous ones (QML, QtQuick, Scripting with QtScript and in QtWebKit, Qt3 vs. Qt4, QGraphicsScene, QItemView, QtDesigner, QtCreator). To me this is too much feature driven and dictated by the progress of other market players. Then I saw these super new technologies contrasted by very basic demo videos in YouTube showing excitement over very basic features which is not very professional – you can check out their channel on http://www.youtube.com/user/QtStudios to get an idea. What I want as a consumer of platform technology building a commercial grade application is that the selected platform is very stable and well thought out. But there is just too much noise coming from Qt and to make matters worse, none of the new features really feels final, it feels more like work in progress and you can never know how long something survives. Last not least, one day down the road Nokia might become a rival again to Apple just as relevant as Google now is. Economically speaking, Nokia already is, but Apple sees Google as its main competitor, not Microsoft and not Nokia. They are just not in the same technology league any more. Apple and Google have the brains now.

    Don’t get me wrong, the Qt guys are doing a good job and they surely do it with a lot of enthusiasm, all I want to say is that Qt is just not the best bet in a commercial hybrid application. That is why I gave up on it.

     
    • Ariya 03:54 on 26.04.2010 Permalink | Reply

      Just because Google Earth is written in Qt and does not have the Mac L&F, you are too quick to conclude that all Qt-based application “loses the capability to provide the eye-candy to the platform”. It’s Google Earth issue, it has nothing to do with Qt. For example, check http://doc.trolltech.com/qq/qq23-layouts.html where special attentions are given so that even the widgets margins and spacings match to those of the native Aqua.

      For “Development of Qt is distributed across Europe”, where did you get this fact? Last time I checked the Qt developers are mostly in Oslo, Berlin, and Brisbane.

      Some further comments on Qt from you are more subjective and hence I will not comment on it, unless you can show more substantial examples, like those of “deprecating previous ones”.

      Also, judging a framework from the reaction of its YouTube channel users is likely a hit-and-miss. You should check Qt Labs instead (and its archive) for a better peek on what happens inside the Qt kitchen.

      • apareis 04:02 on 27.04.2010 Permalink | Reply

        Ariya, thanks for your comment and my apology for being so emotional, but the decision was not an easy one. And there are of course rational and irrational aspects to a decision. We are still people ;)

        You are right, it might actually be possible to create a Qt app with L&F close to the platform standard, with some extra effort, as you already documented with that link. The question is though: at what price? I’m sure Qt does not offer the very special Mac widget types out of the box, so you would have to reprogram them, but these are the ones that make up the platform eye candy. In case of Google Earth you can argue that indeed Google is to blame, but what you then actually say is that Google should create a special Mac OS X version and a special Windows version and a special XYZ version even if they are all Qt based, and then the whole Qt based approach becomes questionable as it moves away from the cross-platform paradigm. Then you might as well just program against the native platform toolkits at perhaps similar costs but with the benefit of real platform nativity. Surely everything can be done, but everything also has a price tag. BTW, do you actually know at least a good example of a well-done Qt application with a decent Mac OS X GUI? I couldn’t find one…

        IMO, Trolltech’s development structure was absolutely appropriate when it was Trolltech, given that most of Qt is open-source and needs community knowledge, marketing and development. That is why Trolltech took the distributed approach and it was fine, but from Nokia I would expect a more headquartered approach in order increase productivity in one of its key products. Don’t get me wrong, they can do what they want, the decision is on my side. Do I trust them enough to base an applications architecture on their product? I don’t, and that’s what my post was about.

        In Qt4 they introduced a whole bunch of new APIs, and even in the subsequent minor versions things have been added quite frequently. The thing is, in a project you program against what’s there, and not against what might come, because you don’t know that. Their approach should be: Do it right from start, especially in a highly dependent environment like Qt. If I make a decision towards Qt today then I must be prepared that they will change things like they did in the past. There will be for instance a QtWebKit 2.0 coming soon introducing perhaps again a different or extended API and QtWebKit will be spun off from Qt completely. These expected changes are simply a project risk, even if they are benefits, but you have to deal with them because you want the functionality, so you end up living in the lifecycle of Qt.That can be ok, but you need to be aware of it.

        You got me not quite right with the videos. I wasn’t referring to the YouTube users, I was referring to the content of the videos. If you take this one for example http://www.youtube.com/user/QtStudios#p/u/12/sQnb7g_jSM4 this tells me that they are excited over very basic (and ugly) things which isn’t quite what I would expect from Nokia. Or this one here: http://www.youtube.com/user/QtStudios#p/u/13/ubW3GM9NzS4 it should be quite clear that long running tasks must be done in a different thread. Those things are just basics and shouldn’t be shown in an official video channel because that leaves room for interpretation where they actually stand.

        • Nitin 14:00 on 07.06.2010 Permalink

          Do you have any example, where such an app was created without involving Qt or any other UI toolkit, but just WebKit?

        • apareis 15:03 on 07.06.2010 Permalink

          No, and I don’t think there is any. I think that is because it seems to be a very expensive approach:

          1) the WebKit build process: All current ports are part of the WebKit build process. You would need to spend a considerable amount of effort on integrating into the WebKit build or to integrate the WebKit build into your own build. Qt has done the latter, and that could be a blueprint for you.

          2) WebKit interaction with the platform: WebKit needs to interact with the platform in many ways: e.g., for networking, drawing, getting user input etc. – those things all need to be provided to WebKit from your app.

          3) code integration: each WebKit wrapper adds code to the WebKit codebase, controlled with #ifdefs, in many cases just a pointer or a conversion function from the wrapper’s string type and such. You would need to do that as well whereas it might be very hard to maintain your extensions outside of the original code base as you still want to consume changes from WebKit downstream. You might be able to do that with git merge. And to create a new port for your app inside the WebKit repository should not be an easy one for administrative or political reasons at WebKit.org.

          That is why it seems to be so much easier to start with an already wrapped WebKit. I’m using Apple’s WebKit framework now. I could also have used Chromium instead but I would lose the iPhone/iPad support so I decided to stick with Apple and since they are the inventor of WebKit they aren’t the worst bet as I think they still set the pace for the other ports and they provide a very powerful integration between the JavaScript engine and the Objective-C platform.

      • MySchizoBuddy 17:12 on 25.11.2010 Permalink | Reply

        Snow leopard widgets differ slightly from the ones in QT. QT widgets are from the older version of mac.

    • Nitin 14:31 on 17.06.2010 Permalink | Reply

      apareis :
      No, and I don’t think there is any. I think that is because it seems to be a very expensive approach:
      1) the WebKit build process: All current ports are part of the WebKit build process. You would need to spend a considerable amount of effort on integrating into the WebKit build or to integrate the WebKit build into your own build. Qt has done the latter, and that could be a blueprint for you.
      2) WebKit interaction with the platform: WebKit needs to interact with the platform in many ways: e.g., for networking, drawing, getting user input etc. – those things all need to be provided to WebKit from your app.
      3) code integration: each WebKit wrapper adds code to the WebKit codebase, controlled with #ifdefs, in many cases just a pointer or a conversion function from the wrapper’s string type and such. You would need to do that as well whereas it might be very hard to maintain your extensions outside of the original code base as you still want to consume changes from WebKit downstream. You might be able to do that with git merge. And to create a new port for your app inside the WebKit repository should not be an easy one for administrative or political reasons at WebKit.org.
      That is why it seems to be so much easier to start with an already wrapped WebKit. I’m using Apple’s WebKit framework now. I could also have used Chromium instead but I would lose the iPhone/iPad support so I decided to stick with Apple and since they are the inventor of WebKit they aren’t the worst bet as I think they still set the pace for the other ports and they provide a very powerful integration between the JavaScript engine and the Objective-C platform.

      Thanks for providing more insight. But if I have existing Hybrid apps developed on Qt-WebKit, and now want to change to Android-WebKit, whether the HTML, CSS, JS code can be used as it is?
      I understand that the C++ code to interface with JS environment would have to be changed. Whats your opinion on this?

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: