//
// 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(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
#pragma once
#include "Common.h"
BEGIN_NAMESPACE
// Runs all the unit tests
void runUnitTests(int a_verbosity);
#if defined(NDEBUG)
# define UNIT_TEST(TestName, Verbose) \
static void UnitTest##TestName() // Ensures test names are unique, even in release
# define CHECK(val) \
((val)) // Ensures abusing this macro will have consistent side-effects
#else
# define UNIT_TEST(TestName, Verbose) \
struct UnitTest##TestName : public UnitTestBase \
{ \
UnitTest##TestName() : UnitTestBase(#TestName, Verbose) {} \
void runTest(); \
} g_run##TestName; \
void UnitTest##TestName::runTest()
# define CHECK(val) \
checkHelper((val), #val, __FILE__, __LINE__)
#endif
// Implementation detail, not intended as part of the user accessed API
struct UnitTestBase
{
UnitTestBase(const char* name, int a_verbosity);
void checkHelper(bool res, const char* str, const char* file, int line);
virtual void runTest() = 0;
const char* testName;
int passCount, failCount, verbose;
UnitTestBase* nextTest;
};
END_NAMESPACE