Browse Source

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

Rusty Russell 15 years ago
parent
commit
118ace351b

+ 1 - 0
ccan/cast/LICENSE

@@ -0,0 +1 @@
+../../licenses/LGPL-3

+ 84 - 0
ccan/cast/_info

@@ -0,0 +1,84 @@
+#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;
+}

+ 131 - 0
ccan/cast/cast.h

@@ -0,0 +1,131 @@
+#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 */

+ 28 - 0
ccan/cast/test/compile_fail-cast_const-sizesame.c

@@ -0,0 +1,28 @@
+#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

+ 17 - 0
ccan/cast/test/compile_fail-cast_const.c

@@ -0,0 +1,17 @@
+#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;
+}

+ 28 - 0
ccan/cast/test/compile_fail-cast_const2-sizesame.c

@@ -0,0 +1,28 @@
+#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

+ 17 - 0
ccan/cast/test/compile_fail-cast_const2.c

@@ -0,0 +1,17 @@
+#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;
+}

+ 28 - 0
ccan/cast/test/compile_fail-cast_const3-sizesame.c

@@ -0,0 +1,28 @@
+#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

+ 17 - 0
ccan/cast/test/compile_fail-cast_const3.c

@@ -0,0 +1,17 @@
+#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;
+}

+ 15 - 0
ccan/cast/test/compile_fail-cast_signed-const.c

@@ -0,0 +1,15 @@
+#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;
+}

+ 28 - 0
ccan/cast/test/compile_fail-cast_signed-sizesame.c

@@ -0,0 +1,28 @@
+#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

+ 16 - 0
ccan/cast/test/compile_fail-cast_signed.c

@@ -0,0 +1,16 @@
+#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;
+}

+ 22 - 0
ccan/cast/test/compile_fail-cast_static-2.c

@@ -0,0 +1,22 @@
+#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

+ 20 - 0
ccan/cast/test/compile_fail-cast_static-3.c

@@ -0,0 +1,20 @@
+#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

+ 16 - 0
ccan/cast/test/compile_fail-cast_static.c

@@ -0,0 +1,16 @@
+#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;
+}