//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/objcclassbuilder.h // Created by : Steinberg, 06/2022 // Description : Objective-C class builder utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include #include #include #include #include //------------------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------------------ template struct ObjCVariable { ObjCVariable (__unsafe_unretained id obj, Ivar ivar) : obj (obj), ivar (ivar) {} ObjCVariable (ObjCVariable&& o) { *this = std::move (o); } ObjCVariable& operator= (ObjCVariable&& o) { obj = o.obj; ivar = o.ivar; o.obj = nullptr; o.ivar = nullptr; return *this; } T get () const { auto offset = ivar_getOffset (ivar); return *reinterpret_cast (((__bridge uintptr_t)obj) + offset); } void set (const T& value) { auto offset = ivar_getOffset (ivar); auto storage = reinterpret_cast (((__bridge uintptr_t)obj) + offset); *storage = value; } private: __unsafe_unretained id obj; Ivar ivar {nullptr}; }; //------------------------------------------------------------------------------------ struct ObjCInstance { ObjCInstance (__unsafe_unretained id obj, Class superClass = nullptr) : obj (obj) { os.super_class = superClass; } template std::optional> getVariable (const char* name) const { if (__strong auto ivar = class_getInstanceVariable (object_getClass (obj), name)) { return {ObjCVariable (obj, ivar)}; } return {}; } template void callSuper (SEL selector, T... args) const { void (*f) (__unsafe_unretained id, SEL, T...) = (void (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper; f (getSuper (), selector, args...); } template R callSuper (SEL selector, T... args) const { R (*f) (__unsafe_unretained id, SEL, T...) = (R (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper; return f (getSuper (), selector, args...); } private: id getSuper () const { if (os.receiver == nullptr) { os.receiver = obj; } if (os.super_class == nullptr) { os.super_class = class_getSuperclass (object_getClass (obj)); } return (__bridge id) (&os); } __unsafe_unretained id obj; mutable objc_super os {}; }; //------------------------------------------------------------------------------------ struct ObjCClassBuilder { ObjCClassBuilder& init (const char* name, Class baseClass); template ObjCClassBuilder& addMethod (SEL selector, Func imp); template ObjCClassBuilder& addIvar (const char* name); ObjCClassBuilder& addProtocol (const char* name); ObjCClassBuilder& addProtocol (Protocol* proto); Class finalize (); private: static Class generateUniqueClass (const std::string& inClassName, Class baseClass); template ObjCClassBuilder& addMethod (SEL selector, Func imp, const char* types); ObjCClassBuilder& addIvar (const char* name, size_t size, uint8_t alignment, const char* types); template static constexpr std::tuple functionArgs (R (*) (T...)) { return std::tuple (); } template static constexpr std::tuple functionArgs (void (*) (T...)) { return std::tuple (); } template static constexpr bool isVoidReturnType (R (*) (T...)) { return false; } template static constexpr bool isVoidReturnType (void (*) (T...)) { return true; } template static std::string encodeFunction (Proc proc) { std::string result; if (isVoidReturnType (proc)) result = "v"; std::apply ([&] (auto&&... args) { ((result += @encode (decltype (args))), ...); }, functionArgs (proc)); return result; } Class cl {nullptr}; Class baseClass {nullptr}; }; //------------------------------------------------------------------------ inline ObjCClassBuilder& ObjCClassBuilder::init (const char* name, Class bc) { baseClass = bc; cl = generateUniqueClass (name, baseClass); return *this; } //------------------------------------------------------------------------------------ inline Class ObjCClassBuilder::generateUniqueClass (const std::string& inClassName, Class baseClass) { std::string className (inClassName); int32_t iteration = 0; while (objc_lookUpClass (className.data ()) != nil) { iteration++; className = inClassName + "_" + std::to_string (iteration); } Class resClass = objc_allocateClassPair (baseClass, className.data (), 0); return resClass; } //----------------------------------------------------------------------------- inline Class ObjCClassBuilder::finalize () { objc_registerClassPair (cl); auto res = cl; baseClass = cl = nullptr; return res; } //----------------------------------------------------------------------------- template inline ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp, const char* types) { auto res = class_addMethod (cl, selector, IMP (imp), types); assert (res == true); (void)res; return *this; } //----------------------------------------------------------------------------- template ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp) { return addMethod (selector, imp, encodeFunction (imp).data ()); } //------------------------------------------------------------------------ template inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name) { return addIvar (name, sizeof (T), static_cast (std::log2 (sizeof (T))), @encode (T)); } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name, size_t size, uint8_t alignment, const char* types) { auto res = class_addIvar (cl, name, size, alignment, types); assert (res == true); (void)res; return *this; } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (const char* name) { if (auto protocol = objc_getProtocol (name)) return addProtocol (protocol); return *this; } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (Protocol* proto) { auto res = class_addProtocol (cl, proto); assert (res == true); (void)res; return *this; } //------------------------------------------------------------------------------------ } // Steinberg