Check If Two Types Are Equal in C++

How to assert two types are equal in c?

How to assert two types are equal in c?

Use _Generic to get you at least mostly there with non-array types.

#define compare_types(T1, T2) _Generic((  (T1){0}  ), \
T2: "Same", \
default: "Different" \
)

#include <stdio.h>
#include <stdint.h>

int main() {
// Same range
printf("%ld %lld\n", LONG_MAX, LLONG_MAX);
// Same size
printf("%zu %zu\n", sizeof (long), sizeof (long long));
// Yet different
printf("%s\n", compare_types(long, long long));

// int64_t is a long on my machine
printf("%s\n", compare_types(long, int64_t));
printf("%s\n", compare_types(long long, int64_t));
}

Output

9223372036854775807 9223372036854775807
8 8
Different
Same
Different

Improved

Further, a stronger compare employs a A vs B and B vs A test. The 2 tests are useful for the controlling expression of _Generic converts arrays to pointers of the first element losing some type information.

#define strong_helper(T1, T2) _Generic(( (T1){0} ), \
T2: 1, \
default: 0 \
)
#define compare_types_strong(T1, T2) (strong_helper(T1,T2) && strong_helper(T2,T1))

printf("%d\n", compare_types_strong(long, int64_t));
printf("%d\n", compare_types_strong(int [3], int *));

Output

1
0

Still troublesome for arrays and void

compare_types_strong(int [3], int [3]) returns 0 as _Generic converted the controlling expression int [3] to a pointer to the first element type (int *).

@PSkocik, in a deleted comment, points out this approach will not work for the incomplete object type void.

Check if two types are equal in C++

You can use std::is_same<T,U>::value from C++11 onwards.

Here, T, and U are the types, and value will be true if they are equivalent, and false if they are not.

Note that this is evaluated at compile time.

See http://en.cppreference.com/w/cpp/types/is_same

Will this code correctly determine if two types are equal?

Fist let's take a look on the three options you gave:

If myObject.GetType.Equals(MyClass)

This will probably result in a error, since the equals expects a System.Type, not a class. A class definition is not a System.Type, but you can retrieve it using the typeof operator. So you could do instance.Equals(typeof(MyClass)), which would return true if the object is of the given class.

If TypeOf(myObject) Is MyClass

Conversely, you can't use typeof with instances, only with classes, so the above code would fail. Also, the is operator automatically checks the typing so you can't do a typeof or a GetType when using it. You should go with if myObject is MyClass, which would return true if myObject can be cast to MyClass. This is different from saying that it's an instance of that type, because it could be that myObject is an instance of a class that inherits from MyClass.

If myObject.GetType() Is MyClass

Again, the is operator already checks the type on both operands, so you should go with if myObject is MyClass.


All that said, I'd like to explain the "theory" behind the type system. I'm no specialist, so I'm giving you a more practical explanation:

  • A class definition label (like MyClass) is not a System.Type. A System.Type is a metadata class that is generated by the CLR to represent the type defined by your label. To retrieve the System.Type related to a certain class definition label, use the typeof operator as follows:

    System.Type MyClassType = typeof(MyClass);
  • On a object instance, you can retrieve the System.Type metadata by calling the method GetType() on it. It will give you an instance of System.Type related to the class that represents the actual instance. This means that if your object is being treated by the compiler as a interface or a base class, .GetType() still gives you the most derived type for that instance.

  • You can compare System.Type in order to check if two objects are instances of the same class, but again, beware that your instance can be of a more derived type; The equality will fail (the System.Typeof a more derived class is different than that of a less derived one).

  • If you need to take inheritance into account, you can use the method IsAssignableFrom, like this:

    BaseClass instance = new DerivedClass();

    System.Type type = instance.GetType();

    if ((typeof(BaseClass)).IsAssignableFrom(type)) // returns true
    {
    }
  • C# and VB.Net gives you two operators that enables you to do type checking on the fly, is and as. is does automatic typing retrieval and is preferred over getting the System.Type yourself. It accounts for inheritance as well:

    DerivedClass instance = new DerivedClass();

    System.Type type = instance.GetType();

    if (instance is BaseClass) // returns true
    {
    }
  • If you need to check the type and cast the object use as:

    DerivedClassinstance = new DerivedClass();

    System.Type type = instance.GetType();

    AnotherClass another = instance as AnotherClass;

    if (another == null) // returns true
    {
    // Do proper error treatment... throw an exception or something
    }

    What you cannot do with as is not perform proper result checking; The problem is that if you don't check it for null and use it, you get an NullReferenceException, which will hide the correct problem (the cast failed). If you are sure you can do the cast, then use a explicit cast:

    DerivedClassinstance = new DerivedClass();

    System.Type type = instance.GetType();

    AnotherClass another = (AnotherClass)instance; // throws

    This will throw an InvalidCastException, so the code will be easier to debug.

How do I check if a variable is of a certain type (compare two types) in C?

Getting the type of a variable is, as of now, possible in C11 with the _Generic generic selection. It works at compile-time.

The syntax is a bit like that for switch. Here's a sample (from this answer):

    #define typename(x) _Generic((x),                                                 \
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
default: "other")

To actually use it for compile-time manual type checking, you can define an enum with all of the types you expect, something like this:

    enum t_typename {
TYPENAME_BOOL,
TYPENAME_UNSIGNED_CHAR,
TYPENAME_CHAR,
TYPENAME_SIGNED_CHAR,
TYPENAME_SHORT_INT,
TYPENAME_UNSIGNED_CHORT_INT,
TYPENAME_INT,
/* ... */
TYPENAME_POINTER_TO_INT,
TYPENAME_OTHER
};

And then use _Generic to match types to this enum:

    #define typename(x) _Generic((x),                                                       \
_Bool: TYPENAME_BOOL, unsigned char: TYPENAME_UNSIGNED_CHAR, \
char: TYPENAME_CHAR, signed char: TYPENAME_SIGNED_CHAR, \
short int: TYPENAME_SHORT_INT, unsigned short int: TYPENAME_UNSIGNED_SHORT_INT, \
int: TYPENAME_INT, \
/* ... */ \
int *: TYPENAME_POINTER_TO_INT, \
default: TYPENAME_OTHER)

How to check if two types are same at compiletime(bonus points if it works with Boost strong typedef)

Use std::is_same. std::is_same<T,U>::value will be true if T and U are the same type, false otherwise.

If you don't have C++11, it's easy to implement as this

template<class T, class U>
struct is_same {
enum { value = 0 };
};

template<class T>
struct is_same<T, T> {
enum { value = 1 };
};

How to check if two types are the same in Common Lisp?

Common Lisp defines SUBTYPEP to compare types. Using that, you can define a type equality comparison function:

(defun type-equal (t1 t2)
(and (subtypep t1 t2)
(subtypep t2 t1)))

For example, there are different way to encode the boolean type, but they cover the same set of values so they are equal:

(type-equal '(member t nil) '(or (eql t) (eql nil)))
=> T, T

There is also a COERCE functions that in a limited way allows to convert a value to another type. This is however not extensible.

Simple cast

Let's define first a new condition for cases where we don't know how to cast a value to a target type. To keep things simple, this error has no associated data:

(define-condition cast-error (error) ())

Then, a rigid but simple definition for lexical-cast can be:

(defun lexical-cast (return-type value)
(coerce (cond
((type-equal return-type 'boolean)
(typecase value
(string (string= value "true"))
(real (/= value 0))
(t (error 'cast-error))))
((subtypep return-type 'real)
(typecase value
(string (read-from-string value))
(boolean (if value 1 0))
(t (error 'cast-error))))
(t
(error 'cast-error)))
return-type))

I wrap the cond form in a coerce form to check the inner result is of the appropriate return-type. This helps detect implementation errors.

The inner form performs multiple-dispatch on the return type (cond) and on the value's type (typecases). You can for example convert from a string to a boolean:

(lexical-cast '(member t nil) "true")
T

Assuming we follow the C convention of 0 being false, here is a cast from a number (notice that the boolean type is expressed differently):

(lexical-cast '(or (eql t) (eql nil)) 0)
NIL

Note also that Common Lisp allows you to define ranges. Here we have an error because reading from the string gives a value outside of the excepted range (careful: reading from a string can be unsafe for some inputs, but this is out of scope here).

(lexical-cast '(integer 0 10) "50")
;; error from COERCE

(lexical-cast '(integer 0 10) "5")
5

Here is another example, casting to a subtype of reals from a boolean:

(lexical-cast 'bit t)
1

Extensible cast

In order to have a generic extensible lexical cast, like PPRINT-DISPATCH, you would need to maintain of table of conversion functions for different types, while ensuring you always select the closest type matching the return-type.

A very simplified way to do that would be to define individual generic conversion functions for some dedicated return types:

;; can be extended for other classes of values
(defgeneric cast-to-boolean (value)
(:method ((v symbol)) v)
(:method ((v integer)) (eql v 1))
(:method ((v string)) (string= v "true")))

Then, you maintain a mapping from types to conversion functions:

;; usually this is hidden behind user-friendly macros

(defvar *type-cast* nil)

;; this could be written instead (set-cast 'boolean #'cast-to-boolean)
;; for example, but here we just set the list directly.

(setf *type-cast*
(acons 'boolean #'cast-to-boolean *type-cast*))

Then, you have a single cast function that looks up the first type that is equal to the return-type, and call the associated function:

(defun gen-type-cast (return-type value)
(let ((f (assoc return-type *type-cast* :test #'type-equal)))
(if f (funcall (cdr f) value) (error 'cast-error))))

For example:

(gen-type-cast '(member t nil) "true")
T

See also

The lisp-types system can be helpful to analyze and simplify type expressions.

Typescript check if type A === type B | type C

This is possible by defining a conditional type that accepts two types and resolves to true when the input types are equal or false otherwise. Then write some code that will throw a compile error when that type is not true.

When either of the types change you'll get a compile error which will ensure you remember to update whichever type is out of sync. This is especially useful when you want to be notified about changes to a type in a different library.

For example:

type IsExact<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;
function assert<T extends true | false>(expectTrue: T) {}

// this will throw a compile error when the two types get out of sync
assert<IsExact<AllKey, KeepKey | IgnoreKey>>(true);

More robust code is a little longer (ex. handling the any type), but it's rolled up in my library here.

import { assert, IsExact } from "conditional-type-checks";

// define or import AllKey, KeepKey, IgnoreKey

assert<IsExact<AllKey, KeepKey | IgnoreKey>>(true);

Another Option

Another not so nice way of doing this is to create two objects of the two types and assign them to each other.

() => {
let allKeys: AllKey;
let otherKeys: KeepKey | IgnoreKey;

// do this in lambdas to prevent the first assignment from changing
// the type of the variable being assigned to
() => allKeys = otherKeys;
() => otherKeys = allKeys;
};

How to compare two typenames for equality in C++?

It's as simple as:

auto c = std::is_same_v<T, SpecialType> ? GivenFunc2(a, b, single, ...)
: GivenFunc1(a, b, single, ...);

If you can't use C++17, replace std::is_same_v<...> with std::is_same<...>::value.

But for this approach to work, both function calls have to be valid for every T you want to use, even if in reality one of them won't be executed.


If it's not the case, you can resort to if constexpr:

your_type_here c;
if constexpr (std::is_same_v<T, SpecialType>)
c = GivenFunc2(a, b, single, ...);
else
c = GivenFunc1(a, b, single, ...);

(This works only in C++17.)



Related Topics



Leave a reply



Submit