Search code examples
c++iostreamobjective-c++ostream

C++ stream log Objective-C objects


I am trying to write a C++ stream logger that is capable of printing objective-C++ variables as well. The problem is that I can't get it to compile because of the template type checking when calling the obj-c method that gives me the description of the object.

I'd like to have some ideas on how to make it happen.

Thanks

#ifdef __OBJC__
//for operator<< on obj-c objects
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

//SFINAE to get whether we have a obj-c class or not
template<class T> struct IsObjectiveCClass {
    using yesT = char (&)[10];
    using noT = char (&)[1];
    static yesT choose(id);
    static noT choose(...);
    static T make();
    enum { value = sizeof(choose(make())) == sizeof(yesT) };
};
#endif

class Logger {

...


        template <typename T>
        Logger &operator <<(T data) {
#if __OBJC__

            if (IsObjectiveCClass<T>::value) {
                ... How to cast (data to obj) without errors? ...    
                stream->ts << [[obj description] UTF8String];
            } else {
#endif
                stream->ts << data;
#if __OBJC__
            }
#endif
        }

Solution

  • try something like this

    template<typename T, typename V = bool>
    struct is_objc_class : std::false_type { };
    
    template<typename T>
    struct is_objc_class<T,
    typename std::enable_if<std::is_convertible<T, id>::value, bool>::type
    > : std::true_type { };
    
    template <
    class T,
    class = typename std::enable_if<is_objc_class<T>::value>::type
    >
    std::ostream& operator<< (std::ostream& stream, T const & t) {
        stream << [[t description] UTF8String];
        return stream;
    }
    
    struct Logger
    {
        Logger(std::ostream &s):stream(s){}
        std::ostream &stream;
    
        // for objc class
        template <class T>
        typename std::enable_if<is_objc_class<T>::value, Logger&>::type
        operator<< (T const & t) {
            stream << [[t description] UTF8String];
            return *this;
        }
    
        // for everything else
        template <class T>
        typename std::enable_if<!is_objc_class<T>::value, Logger &>::type
        operator<< (T const & t) {
            stream << t;
            return *this;
        }
    };
    
    int main(int argc, char *argv[])
    {
        std::cout << std::boolalpha
        << is_objc_class<id>::value << std::endl
        << is_objc_class<int>::value << std::endl
        << is_objc_class<NSString *>::value << std::endl
        << @"test" << std::endl
        << @[@1] << std::endl
        ;
    
        Logger l(std::cout);
    
        l << @"test" << "test2" << @1 << 1;
    }