Newer
Older
Import / research / ui / toolkit / src / ObjectiveC.h
//
//  ObjectiveC.h
//  Objective-C Wrapper code to make it a bit easier to
//  call ObjC code directly and efficiently from C++
//
//  Created by John Ryland (jryland@xiaofrog.com) on 06/12/2017.
//  Copyright 2017 InvertedLogic. All rights reserved.
//
#pragma once
#include <string>
#include <unordered_map>
#include <objc/message.h>
#include <objc/runtime.h>


namespace ObjectiveC
{


class Method;


inline SEL methodCache(const char* funcName) 
{
  static std::unordered_map<std::string, SEL> cache;
  if (cache.find(funcName) == cache.end())                
    cache[funcName] = sel_getUid(funcName);
  return cache[funcName];
}


inline id classCache(const char* className) 
{
  static std::unordered_map<std::string, id> cache;
  if (cache.find(className) == cache.end())                
    cache[className] = (id)objc_getClass(className);
  return cache[className];
}


class Object
{
public:
  Object(const char* className) : m_object((id)classCache(className)) {}
  Object(id a_obj) : m_object(a_obj) {}

  Method operator[](const char* func);

  template <typename T>
  T get(const char* propertyName)
  {
    //printf("getting %s property from --%s--\n\n\n\n", propertyName, object_getClassName(m_object));
    T val{ 0 };
    object_getInstanceVariable(m_object, propertyName, (void**)&val);
    //Ivar v = 
    //printf("ivar name: --%s--\n", ivar_getName(v));
    return val;
  }

  template <typename T>
  void set(const char* propertyName, T val)
  {
    object_setInstanceVariable(m_object, propertyName, (void*)val);
  }

  id m_object;
};


class Method
{
public:
  Method(id a_obj, SEL a_sel) : m_object(a_obj), m_sel(a_sel) {}

  template <typename ...Ts>
  Object operator()(Ts... args)
  {
    return Object((*(id(*)(id, SEL, ...))&objc_msgSend)(m_object, m_sel, args...));
  }

  // Calling ObjC from C++ isn't very type safe, it is up to the caller to
  // know/check what the appropriate input and output types are for
  // the given function. Preferably a given class would be wrapped to
  // provide type-safe method calling
  template <typename R, typename ...Ts>
  R call(Ts... args)
  {
    return (*(R(*)(id, SEL, ...))&objc_msgSend)(m_object, m_sel, args...);
  }
private:
  id m_object;
  SEL m_sel;
};


inline Method Object::operator[](const char* func)
{
  return Method(m_object, methodCache(func));
}


class AutoReleasePool
{
public:
  AutoReleasePool()
    : m_pool("NSAutoreleasePool")
  {
    m_pool = m_pool["alloc"]()["init"]();
  }
  ~AutoReleasePool()
  {
    m_pool["release"]();
  }
private:
  Object m_pool;
};


} // namespace ObjectiveC