Obj-C++: template metafunction for recognizing Objective-C classes? - objective-c

Using Objective-C++, can I write a C++ IsObjectiveCClass<T> template metafunction such that IsObjectiveCClass<T>::value is true if and only if T is an Objective-C class?
Exactly what are ObjC classes from the viewpoint of the C / C++ subset of the language? When used in a C / C++ context, MyClass* pointers seem to behave like ordinary C pointers; does that mean that MyClass is also a C type?

Here is a simplistic solution that should work in most (if not all? Can anyone think of when this might fail?) cases (it uses clang 3.0 via xcode 4.2 - use typedefs instead of using aliases for earlier clang versions):
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) };
};

You can read my most recent rant about ObjC++ in this question. Avoid it as much as you can possibly get away with. Definitely don't try to integrate Objective-C into C++ template metaprogramming. The compiler might actually rip a hole in space.
Hyperbole aside, what you're trying to do is likely impossible. Objective-C classes are just structs. (C++ classes actually just structs too.) There's not much compile-time introspection available.
An id is a C pointer to a struct objc_object. At runtime, every object is an id, no matter its class.
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;

As with the accepted answer, you can test whether the type is convertible to id, in C++17:
template <typename T>
struct is_objc_ptr : std::integral_constant<bool,
std::is_convertible_v<T, id> && !std::is_null_pointer_v<T>> {};
template <typename T>
constexpr bool is_objc_ptr_v = is_objc_ptr<T>::value;
Testing:
static_assert(!is_objc_ptr_v<nullptr_t>);
static_assert(!is_objc_ptr_v<int>);
static_assert(!is_objc_ptr_v<char *>);
static_assert(is_objc_ptr_v<id>);
static_assert(is_objc_ptr_v<NSObject *>);
I don't know of a way to discover ObjC inheritance relationships at compile-time; in theory they're changeable at runtime so you would have to query the runtime.

If you look at the implementation of the C++ STL library in Xcode, you can follow the template specialization models of others like std::is_integral or std::is_floating_point:
template <class T> struct isObjcObject : public std::false_type { };
template <> struct isObjcObject<id> : public std::true_type { };
where std::false_type and std::true_type are defined in the <type_traits> header file.
If for whatever reason you don't have std::false_type and std::true_type (depending on your C++ version), you can define them yourself as such:
template<bool B> struct boolean_constant { static constexpr const bool value = B; };
template <class T> struct isObjcObject : public boolean_constant<false> { };
template <> struct isObjcObject<id> : public boolean_constant<true> { };
Note that you can also do this for Objective-C classes too:
template <class T> struct isObjcClass : public std::false_type { };
template <> struct isObjcClass<Class> : public std::true_type { };

I would create a template specialisation for 'id' and 'NSObject*', but you'll always be working against the language because the ObjC type system is not the C++ type system.

Similar to Doug's answer, but slightly simpler:
template<typename T>
inline constexpr bool is_objc_v = std::is_convertible_v<id,T>;
Checking that id is convertible to T – instead of the other way around – avoids false positives for C++ types which have a user-defined implicit conversion to an Obj-C type.

Related

How to use a trait type as an argument to an optionally compiled member function of a class template?

I would like to define a member function in a class template only if traits contain type and use type as its argument like that:
struct A {};
struct B { using type = int; };
template <typename T>
concept has_type = requires { typename T::type; };
template <typename Traits>
struct Handler
{
void set(typename Traits::type)
requires has_type<Traits>
{}
};
Unfortunately, typename Traits::type is parsed before the requires clause and it fails to compile if type does not exist (struct A):
<source>:19:14: error: no type named 'type' in 'struct A'
19 | void set(typename Traits::type)
I could only come up with this alternative, which looks excessively cumbersome:
template <typename Traits>
struct Handler
{
template <typename Type>
requires has_type<Traits> && std::same_as<Type, typename Traits::type>
void set(Type)
{}
};
It also fails to compile under clang (although I expected it to short-circuit), but gcc compiles it correctly:
<source>:20:78: error: no type named 'type' in 'A'
requires has_type<Traits> && std::same_as<Type, typename Traits::type>
Full example.
Questions
What is the idiomatic way to define set depending on whether Traits class contains type?
Why does clang try to evaluate std::same_as if requires has_type failed?
Who is right, clang or gcc?
A class template member function is constrainable, but is not SFINAE-aware. The idiomatic solution (which, inter alia, works even under C++17) is to make set a (class template member) function template accessing the type member type alias via (type-dependent) SFINAE context:
template<int..., class U = Traits>
void set(typename U::type);
or:
template<std::same_as<Traits> U = Traits>
void set(typename U::type);
Example.
wrt 2., 3., see How to use a trait type as an argument to an optionally compiled member function of a class template?

Test to detect if a template type in C++/CLI is managed or unmanaged

I have some template code compiled with /clr, something like the following
template <typename T> void foo( )
{
}
and am wondering if it's possible to detect if the T is a managed type or an unmanaged one. So something like
template <typename T> void foo( )
{
constexpr bool b = is_managed_type<T>;
}
If have tried all the std library type traits and those mentioned here https://learn.microsoft.com/en-us/cpp/extensions/compiler-support-for-type-traits-cpp-component-extensions?view=vs-2019 but have not found anything that works in all cases. A particular instance of this problem is to tell if an enum is a managed enum or an unmanaged one. So is it possible to implement a type trait like this?
Added 9/23/19
I did make some progress on a is_managed_type trait. I have the following
int check( System::Object^ );
template <typename T, typename U = void> using converts_to_system_object_t = decltype(check(std::declval<T>()));
template<typename T, typename = void> struct is_managed_type: std::false_type {};
template<typename T> struct is_managed_type<T, converts_to_system_object_t<T>> : std::true_type { };
The basic idea is to test if for given type can you call a function that takes os System::Object^. Note I did try things like std::is_base_of and std::is_convertible but they all had problems.
However this seem to return false for managed enums which is a bit bizzare to me

Convert a Objective C Self Referential Struct to Swift

Self referential Structs are possible in objective C. Eg:
typedef struct Sample {
struct Sample* first;
struct Sample* second;
struct Sample* third;
} SampleStruct;
The swift conversion looks something like
struct Sample {
var first: Sample?
var second: Sample?
var third: Sample?
}
typealias SampleStruct = Sample
But it throws a compiler error saying "Value type 'Sample' cannot have a stored property that references itself".
How do i convert the self referential struct to swift?
You know you cannot define this sort of struct in Objective-C.
typedef struct Sample {
struct Sample first;
struct Sample second;
struct Sample third;
} SampleStruct;
(Swift adds a hidden isNil... field for each Optional, but it's not a big difference.)
If you want to define an exact equivalent to your Objective-C code, you need to use pointers, as in the original code.
struct Sample {
var first: UnsafeMutablePointer<Sample>?
var second: UnsafeMutablePointer<Sample>?
var third: UnsafeMutablePointer<Sample>?
}
Better consider using class as commented.

How to create an enum for both Swift and ObjC with standard naming?

I am writing an OS X/iOS framework in Objective-C, and I would like for the framework to be useful for developers using either Objective-C or Swift.
In normal Objective-C enums are defined like this (this example is taken directly from Apple's own UIView class reference).
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
To make this enum Swift-friendly, my understanding is that it should be declared like this.
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurve_EaseInOut,
UIViewAnimationCurve_EaseIn,
UIViewAnimationCurve_EaseOut,
UIViewAnimationCurve_Linear
};
This allows the enum to be accessed in the style of let curve: UIViewAnimationCurve = .EaseInOut from Swift.
My problem is that the NS_ENUM and underscore method produces strangely named enums when used from Objective-C. The NS_ENUM method allows dot notation to be used from Swift, but it also means that any ObjC code will need to use an underscore in the enumerated name, which is undesirable.
How can I allow dot notation for Swift while still preserving Objective-C style naming conventions for within ObjC code?
You simply follow the usual convention – no underscoring is necessary. Swift compiler is smart enough to just cut the common prefix out (the part that matches the enum type name). You do have to use an NS_ENUM for the enum to be made visible to Swift, but it's good practice anyway.
Case in point, for instance UIViewAnimationCurve is defined in an Objective-C header in just the form you describe in your first code example and works just fine in Swift:
If you define it like this:
typedef long TrafficLightColor NS_TYPED_ENUM;
TrafficLightColor const TrafficLightColorRed;
TrafficLightColor const TrafficLightColorYellow;
TrafficLightColor const TrafficLightColorGreen;
if get compiled to swift like this:
struct TrafficLightColor: RawRepresentable, Equatable, Hashable {
typealias RawValue = Int
init(rawValue: RawValue)
var rawValue: RawValue { get }
static var red: TrafficLightColor { get }
static var yellow: TrafficLightColor { get }
static var green: TrafficLightColor { get }
}
Looks like what you need, anyway take a look at: https://itunes.apple.com/us/book/using-swift-with-cocoa-and-objective-c-swift-4-1-beta/id1002624212?mt=11

Is it possible to create and use custom System::String^ extension methods in C++/CLI?

I tried making an extension to the built-in String class using C++/CLI, and using it from C++/CLI without success.
Here's the simplest I can boil it down to:
[System::Runtime::CompilerServices::Extension]
public ref class MyStringExtensions abstract sealed {
public:
[System::Runtime::CompilerServices::Extension]
static bool TestMethod(System::String^ str) { return false; }
};
Now, when I try to use this in other C++/CLI code, I get a compiler message indicating that TestMethod is not a method of String.
String^ foo = gcnew ...
...
blah = foo->TestMethod(); // compile-error
Any ideas?
C++ doesn't have extension methods.
But it does have ADL (Argument-dependent lookup, also known as Koenig lookup) which is arguably even nicer.