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. ASystem.Type
is a metadata class that is generated by the CLR to represent the type defined by your label. To retrieve theSystem.Type
related to a certain class definition label, use thetypeof
operator as follows:System.Type MyClassType = typeof(MyClass);
On a object instance, you can retrieve the
System.Type
metadata by calling the methodGetType()
on it. It will give you an instance ofSystem.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 (theSystem.Type
of 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
andas
.is
does automatic typing retrieval and is preferred over getting theSystem.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 anNullReferenceException
, 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; // throwsThis 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 (typecase
s). 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
After Sending a Lot, My Send() Call Causes My Program to Stall Completely. How Is This Possible
Template Assignment Operator Overloading Mystery
Is Gcc Considering Builtins of Non-Constant Expression Functions to Be Constant Expressions
Documenting Preprocessor Defines in Doxygen
How to Create a Temporary Text File in C++
How to Control Memory Allocation Strategy in Third Party Library Code
Can You Make a Computed Goto in C++
What Is The Correct Behavior of Pthread_Mutex_Destroy When Destroying a Locked Mutex
Allowing Access to Private Members
Use Static_Assert to Check Types Passed to MACro
Rotating a 2D Pixel Array by 90 Degrees
Libstdc++ Glibcxx Version Errors
How to Implement a Video Widget in Qt That Builds Upon Gstreamer
C++ Arrays as Function Arguments