Commit 118ace35 authored by Rusty Russell's avatar Rusty Russell

cast: new limited cast package inspired by Jan Engelhardt's libhx.

parent ce6eef41
../../licenses/LGPL-3
\ No newline at end of file
#include <string.h>
#include "config.h"
/**
* cast - routines for safer casting.
*
* Often you want to cast in a limited way, such as removing a const or
* switching between integer types. However, normal casts will work on
* almost any type, making them dangerous when the code changes.
*
* These C++-inspired macros serve two purposes: they make it clear the
* exact reason for the cast, and they also (with some compilers) cause
* errors when misused.
*
* Based on Jan Engelhardt's libHX macros: http://libhx.sourceforge.net/
*
* Author: Jan Engelhardt
* Maintainer: Rusty Russell <rusty@rustcorp.com.au>
* License: LGPL
*
* Example:
* // Given "test" contains "3 t's in 'test string'
* #include <ccan/cast/cast.h>
* #include <stdint.h>
* #include <stdio.h>
*
* // Find char @orig in @str, if @repl, replace them. Return number.
* static size_t find_chars(char *str, char orig, char repl)
* {
* size_t i, count = 0;
* for (i = 0; str[i]; i++) {
* if (str[i] == orig) {
* count++;
* if (repl)
* str[i] = repl;
* }
* }
* return count;
* }
*
* // Terrible hash function.
* static uint64_t hash_string(const unsigned char *str)
* {
* size_t i;
* uint64_t hash = 0;
* for (i = 0; str[i]; i++)
* hash += str[i];
* return hash;
* }
*
* int main(int argc, char *argv[])
* {
* uint64_t hash;
*
* // find_chars wants a non-const string, but doesn't
* // need it if repl == 0.
* printf("%zu %c's in 'test string'\n",
* find_chars(cast_const(char *, "test string"),
* argv[1][0], 0),
* argv[1][0]);
*
* // hash_string wants an unsigned char.
* hash = hash_string(cast_signed(unsigned char *, argv[1]));
*
* // Need a long long to hand to printf.
* printf("Hash of '%s' = %llu\n", argv[1],
* cast_static(unsigned long long, hash));
* return 0;
* }
*
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/build_assert\n");
return 0;
}
return 1;
}
#ifndef CCAN_CAST_H
#define CCAN_CAST_H
#include "config.h"
#include <ccan/build_assert/build_assert.h>
/**
* cast_signed - cast a (const) char * to/from (const) signed/unsigned char *.
* @type: some char * variant.
* @expr: expression (of some char * variant) to cast.
*
* Some libraries insist on an unsigned char in various places; cast_signed
* makes sure (with suitable compiler) that the expression you are casting
* only differs in signed/unsigned, not in type or const-ness.
*/
#define cast_signed(type, expr) \
((type)(expr) \
+ EXPR_BUILD_ASSERT(cast_sign_compatible(type, (expr))))
/**
* cast_const - remove a const qualifier from a pointer.
* @type: some pointer type.
* @expr: expression to cast.
*
* This ensures that you are only removing the const qualifier from an
* expression. The expression must otherwise match @type.
*
* If @type is a pointer to a pointer, you must use cast_const2 (etc).
*
* Example:
* // Dumb open-coded strstr variant.
* static char *find_needle(const char *haystack)
* {
* size_t i;
* for (i = 0; i < strlen(haystack); i++)
* if (memcmp("needle", haystack+i, strlen("needle")) == 0)
* return cast_const(char *, haystack+i);
* return NULL;
* }
*/
#define cast_const(type, expr) \
((type)(expr) \
+ EXPR_BUILD_ASSERT(cast_const_compat1((expr), type)))
/**
* cast_const2 - remove a const qualifier from a pointer to a pointer.
* @type: some pointer to pointer type.
* @expr: expression to cast.
*
* This ensures that you are only removing the const qualifier from an
* expression. The expression must otherwise match @type.
*/
#define cast_const2(type, expr) \
((type)(expr) \
+ EXPR_BUILD_ASSERT(cast_const_compat2((expr), type)))
/**
* cast_const3 - remove a const from a pointer to a pointer to a pointer..
* @type: some pointer to pointer to pointer type.
* @expr: expression to cast.
*
* This ensures that you are only removing the const qualifier from an
* expression. The expression must otherwise match @type.
*/
#define cast_const3(type, expr) \
((type)(expr) \
+ EXPR_BUILD_ASSERT(cast_const_compat3((expr), type)))
/**
* cast_static - explicit mimic of implicit cast.
* @type: some type.
* @expr: expression to cast.
*
* This ensures that the cast is not to or from a pointer: it can only be
* an implicit cast, such as a pointer to a similar const pointer, or between
* integral types.
*/
#if HAVE_COMPOUND_LITERALS
#define cast_static(type, expr) \
((struct { type x; }){(expr)}.x)
#else
#define cast_static(type, expr) \
((type)(expr))
#endif
/* Herein lies the gcc magic to evoke compile errors. */
#if HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF
#define cast_sign_compatible(t, e) \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(t), char *) || \
__builtin_types_compatible_p(__typeof__(t), signed char *) || \
__builtin_types_compatible_p(__typeof__(t), unsigned char *), \
/* if type is not const qualified */ \
__builtin_types_compatible_p(__typeof__(e), char *) || \
__builtin_types_compatible_p(__typeof__(e), signed char *) || \
__builtin_types_compatible_p(__typeof__(e), unsigned char *), \
/* and if it is... */ \
__builtin_types_compatible_p(__typeof__(e), const char *) || \
__builtin_types_compatible_p(__typeof__(e), const signed char *) || \
__builtin_types_compatible_p(__typeof__(e), const unsigned char *) ||\
__builtin_types_compatible_p(__typeof__(e), char *) || \
__builtin_types_compatible_p(__typeof__(e), signed char *) || \
__builtin_types_compatible_p(__typeof__(e), unsigned char *) \
)
#define cast_const_strip1(expr) \
__typeof__(*(struct { int z; __typeof__(expr) x; }){0}.x)
#define cast_const_strip2(expr) \
__typeof__(**(struct { int z; __typeof__(expr) x; }){0}.x)
#define cast_const_strip3(expr) \
__typeof__(***(struct { int z; __typeof__(expr) x; }){0}.x)
#define cast_const_compat1(expr, type) \
__builtin_types_compatible_p(cast_const_strip1(expr), \
cast_const_strip1(type))
#define cast_const_compat2(expr, type) \
__builtin_types_compatible_p(cast_const_strip2(expr), \
cast_const_strip2(type))
#define cast_const_compat3(expr, type) \
__builtin_types_compatible_p(cast_const_strip3(expr), \
cast_const_strip3(type))
#else
#define cast_sign_compatible(type, expr) \
(sizeof(*(type)0) == 1 && sizeof(*(expr)) == 1)
#define cast_const_compat1(expr, type) \
(sizeof(*(expr)) == sizeof(*(type)0))
#define cast_const_compat2(expr, type) \
(sizeof(**(expr)) == sizeof(**(type)0))
#define cast_const_compat3(expr, type) \
(sizeof(***(expr)) == sizeof(***(type)0))
#endif
#endif /* CCAN_CAST_H */
#include <ccan/cast/cast.h>
#include <stdlib.h>
/* Note: this *isn't* sizeof(char) on all platforms. */
struct char_struct {
char c;
};
int main(int argc, char *argv[])
{
char *uc;
const
#ifdef FAIL
struct char_struct
#else
char
#endif
*p = NULL;
uc = cast_const(char *, p);
return 0;
}
#ifdef FAIL
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_const can only use size"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *uc;
const
#ifdef FAIL
int
#else
char
#endif
*p = NULL;
uc = cast_const(char *, p);
return 0;
}
#include <ccan/cast/cast.h>
#include <stdlib.h>
/* Note: this *isn't* sizeof(char) on all platforms. */
struct char_struct {
char c;
};
int main(int argc, char *argv[])
{
char **uc;
const
#ifdef FAIL
struct char_struct
#else
char
#endif
**p = NULL;
uc = cast_const2(char **, p);
return 0;
}
#ifdef FAIL
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_const can only use size"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char **uc;
const
#ifdef FAIL
int
#else
char
#endif
**p = NULL;
uc = cast_const2(char **, p);
return 0;
}
#include <ccan/cast/cast.h>
#include <stdlib.h>
/* Note: this *isn't* sizeof(char) on all platforms. */
struct char_struct {
char c;
};
int main(int argc, char *argv[])
{
char ***uc;
const
#ifdef FAIL
struct char_struct
#else
char
#endif
***p = NULL;
uc = cast_const3(char ***, p);
return 0;
}
#ifdef FAIL
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_const can only use size"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char ***uc;
const
#ifdef FAIL
int
#else
char
#endif
***p = NULL;
uc = cast_const3(char ***, p);
return 0;
}
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned char *uc;
#ifdef FAIL
const
#endif
char
*p = NULL;
uc = cast_signed(unsigned char *, p);
return 0;
}
#include <ccan/cast/cast.h>
#include <stdlib.h>
/* Note: this *isn't* sizeof(char) on all platforms. */
struct char_struct {
char c;
};
int main(int argc, char *argv[])
{
unsigned char *uc;
#ifdef FAIL
struct char_struct
#else
char
#endif
*p = NULL;
uc = cast_signed(unsigned char *, p);
return 0;
}
#ifdef FAIL
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_signed can only use size"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned char *uc;
#ifdef FAIL
int
#else
char
#endif
*p = NULL;
uc = cast_signed(unsigned char *, p);
return 0;
}
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *c;
#ifdef FAIL
long
#else
char
#endif
*p = 0;
c = cast_static(char *, p);
return 0;
}
#ifdef FAIL
#if !HAVE_COMPOUND_LITERALS
#error "Unfortunately we don't fail if cast_static is a noop"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *c;
#ifdef FAIL
const
#endif
char *p = 0;
c = cast_static(char *, p);
return 0;
}
#ifdef FAIL
#if !HAVE_COMPOUND_LITERALS
#error "Unfortunately we don't fail if cast_static is a noop"
#endif
#endif
#include <ccan/cast/cast.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char c;
#ifdef FAIL
char *
#else
long
#endif
x = 0;
c = cast_static(char, x);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment