From 952241a4269c6b0496efd2f55ddc56144652a7b2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 21 Oct 2014 14:55:38 +0100 Subject: [PATCH] more cplusplus hackery --- cplusplus/VImage.cc | 162 ++++++++++++++++++++++-- cplusplus/include/vips/VImage.h | 16 +-- cplusplus/include/vips/vips-operators.h | 2 +- cplusplus/include/vips/vips8 | 4 +- cplusplus/vips-operators.cc | 4 +- libvips/include/vips/basic.h | 19 +++ libvips/include/vips/object.h | 3 +- libvips/include/vips/operation.h | 18 +-- libvips/iofuncs/object.c | 8 +- libvips/iofuncs/operation.c | 67 +++++++--- 10 files changed, 237 insertions(+), 66 deletions(-) diff --git a/cplusplus/VImage.cc b/cplusplus/VImage.cc index 3875ed52..5893adce 100644 --- a/cplusplus/VImage.cc +++ b/cplusplus/VImage.cc @@ -37,10 +37,11 @@ #include #include -#include #include -#include +#include + +#include "include/vips/vips8" #ifdef WITH_DMALLOC #include @@ -54,7 +55,9 @@ VIPS_NAMESPACE_START /* Useful to have these as namespaced C++ functions. */ -void init( const char *argv0 ) +void +init( const char *argv0 ) + throw( VError ) { if( vips_init( argv0 ) ) throw VError(); @@ -70,14 +73,139 @@ void thread_shutdown() vips_thread_shutdown(); } +static void +call_get( GParamSpec *pspec, void *arg ) +{ + /* We've read out of the VipsObject to the pointer. If it's a + * VipsImage, we need to box it. + */ + if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) { + VImage *image = new VImage( *((VipsImage **) arg) ); + *((VImage *) arg) = *image; + } +} +static void +call_set( GParamSpec *pspec, GValue *value ) +{ + if( G_VALUE_HOLDS( value, VIPS_TYPE_IMAGE ) ) { + /* A VImage has been written to the GValue, extract the VImage + * and swap it for the underlying VipsImage* pointer. + */ + VImage *image = static_cast( + g_value_peek_pointer( value ) ); + g_value_set_object( value, (gpointer) (image->image()) ); + } +} +/* Some systems do not have va_copy() ... this might work (it does on MSVC, + * apparently). + * + * FIXME ... this should be in configure.in + */ +#ifndef va_copy +#define va_copy(d,s) ((d) = (s)) +#endif + +void +call( const char *operation_name, ... ) + throw( VError ) +{ + VipsCollect collect; + VipsOperation *operation; + int result; + va_list required; + va_list optional; + + if( !(operation = vips_operation_new( operation_name )) ) + throw VError(); + + /* We have to break the va_list into separate required and optional + * components. + * + * Note the start, grab the required, then copy and reuse. + */ + va_start( required, operation_name ); + + va_copy( optional, required ); + + VIPS_ARGUMENT_FOR_ALL( operation, + pspec, argument_class, argument_instance ) { + + g_assert( argument_instance ); + + if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) { + VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, + optional ); + + VIPS_ARGUMENT_COLLECT_GET( pspec, argument_class, + optional ); + + VIPS_ARGUMENT_COLLECT_END + } + } VIPS_ARGUMENT_FOR_ALL_END + + collect.get = call_get; + collect.set = call_set; + result = vips_call_required_optional( &operation, + &collect, required, optional ); + + va_end( required ); + va_end( optional ); + + /* Build failed: junk args. + */ + if( result ) + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + + /* The operation we have built should now have been reffed by one of + * its arguments or have finished its work. Either way, we can unref. + */ + g_object_unref( operation ); + + if( result ) + throw VError(); +} + +int +call_split( const char *operation_name, va_list optional, ... ) +{ + VipsOperation *operation; + va_list required; + VipsCollect collect; + int result; + + if( !(operation = vips_operation_new( operation_name )) ) + throw VError(); + + va_start( required, optional ); + + collect.get = call_get; + collect.set = call_set; + result = vips_call_required_optional( &operation, + &collect, required, optional ); + + va_end( required ); + + /* Build failed: junk args. + */ + if( result ) + vips_object_unref_outputs( VIPS_OBJECT( operation ) ); + + /* The operation we have built should now have been reffed by one of + * its arguments or have finished its work. Either way, we can unref. + */ + g_object_unref( operation ); + + if( result ) + throw VError(); +} // see vips_image_new_from_file() VImage::VImage( const char *name, ... ) - __attribute__((sentinel)) throw( VError ) + throw( VError ) { char filename[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; @@ -85,8 +213,6 @@ VImage::VImage( const char *name, ... ) va_list ap; int result; - vips_check_init(); - vips__filename_split8( name, filename, option_string ); if( !(operation_name = vips_foreign_find_load( filename )) ) throw VError(); @@ -102,14 +228,13 @@ VImage::VImage( const char *name, ... ) // see vips_image_new_from_buffer() VImage::VImage( void *buffer, size_t length, const char *option_string, ... ) + throw( VError ) { const char *operation_name; VipsBlob *blob; va_list ap; int result; - vips_check_init(); - if( !(operation_name = vips_foreign_find_load_buffer( buffer, length )) ) throw VError(); @@ -130,7 +255,7 @@ VImage::VImage( void *buffer, size_t length, const char *option_string, ... ) } // see vips_image_write_to_file() -void VImage::write( const char *name, ... ) +void VImage::write_to_file( const char *name, ... ) throw( VError ) { char filename[VIPS_PATH_MAX]; @@ -145,7 +270,7 @@ void VImage::write( const char *name, ... ) va_start( ap, name ); result = vips_call_split_option_string( operation_name, option_string, - ap, this.im, filename ); + ap, this->im, filename ); va_end( ap ); if( result ) @@ -153,7 +278,7 @@ void VImage::write( const char *name, ... ) } // see vips_image_write_to_buffer() -void *VImage::write( const char *suffix, size_t *size, ... ) +void *VImage::write_to_buffer( const char *suffix, size_t *size, ... ) throw( VError ) { char filename[VIPS_PATH_MAX]; @@ -170,7 +295,7 @@ void *VImage::write( const char *suffix, size_t *size, ... ) va_start( ap, size ); result = vips_call_split_option_string( operation_name, option_string, - ap, this.im, &blob ); + ap, this->im, &blob ); va_end( ap ); if( result ) @@ -194,3 +319,16 @@ void *VImage::write( const char *suffix, size_t *size, ... ) #include "vips-operators.cc" VIPS_NAMESPACE_END + + +int +main( int argc, char **argv ) +{ + vips8::init( argv[0] ); + + vips8::VImage x( "/home/john/pics/k2.jpg", NULL ); + + printf( "width = %d\n", x.width() ); + + return( 0 ); +} diff --git a/cplusplus/include/vips/VImage.h b/cplusplus/include/vips/VImage.h index 4d32430c..f102f454 100644 --- a/cplusplus/include/vips/VImage.h +++ b/cplusplus/include/vips/VImage.h @@ -79,26 +79,22 @@ public: VImage( const char *filename, const char *mode = "r" ) throw( VError ) { - vips_check_init(); - if( !(im = vips_image_new_mode( filename, mode )) ) throw VError(); } // see vips_image_new_from_file() VImage( const char *name, ... ) - __attribute__((sentinel)) throw( VError ); + throw( VError ) __attribute__((sentinel)); // see vips_image_new_from_buffer() VImage( void *buffer, size_t length, const char *option_string, ... ) - __attribute__((sentinel)) throw( VError ); + throw( VError ) __attribute__((sentinel)); // see vips_image_new_from_memory() VImage( void *data, size_t size, int width, int height, int bands, VipsBandFormat format ) throw( VError ) { - vips_check_init(); - if( !(im = vips_image_new_from_memory( data, size, width, height, bands, format )) ) throw VError(); @@ -154,15 +150,15 @@ public: throw VError(); } - void write( const char *name, ... ) - throw( VError ); + void write_to_file( const char *name, ... ) + throw( VError ) { - if( vips_image_write_to_file( im, out.im ) ) + if( vips_image_write_to_file( im, name, NULL ) ) throw VError(); } // see vips_image_write_to_buffer() - void *write( const char *suffix, size_t *size, ... ) + void *write_to_buffer( const char *suffix, size_t *size, ... ) throw( VError ); // also need diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h index dd9d2444..4cf76864 100644 --- a/cplusplus/include/vips/vips-operators.h +++ b/cplusplus/include/vips/vips-operators.h @@ -1,3 +1,3 @@ VImage add( VImage add_in2, ... ) - __attribute__((sentinel)) throw( VError ); + throw( VError ) __attribute__((sentinel)); diff --git a/cplusplus/include/vips/vips8 b/cplusplus/include/vips/vips8 index 6a5f8147..fa08fd7e 100644 --- a/cplusplus/include/vips/vips8 +++ b/cplusplus/include/vips/vips8 @@ -37,7 +37,7 @@ #define VIPS_NAMESPACE_START namespace vips8 { #define VIPS_NAMESPACE_END } -#include -#include +#include "VError.h" +#include "VImage.h" #endif /*VIPS_CPLUSPLUS*/ diff --git a/cplusplus/vips-operators.cc b/cplusplus/vips-operators.cc index 9bd8b529..27e360f8 100644 --- a/cplusplus/vips-operators.cc +++ b/cplusplus/vips-operators.cc @@ -1,11 +1,11 @@ -VImage VImage::add( VImage in2 ) +VImage VImage::add( VImage in2, ... ) throw( VError ) { va_list ap; VImage out; int result; - va_start( ap, out ); + va_start( ap, in2 ); result = call_split( "add", ap, this, in2, &out ); va_end( ap ); diff --git a/libvips/include/vips/basic.h b/libvips/include/vips/basic.h index 633f4f31..ac47b843 100644 --- a/libvips/include/vips/basic.h +++ b/libvips/include/vips/basic.h @@ -59,6 +59,25 @@ typedef void *(*VipsSListMap4Fn)( void *item, typedef void *(*VipsSListFold2Fn)( void *item, void *a, void *b, void *c ); +/* The value has been read from the VipsOperation and written to the + * arg pointer. @arg is eg. gboolean* for a bool value. + */ +typedef void (*VipsCollectGet)( GParamSpec *pspec, void *arg ); + +/* The value has been read from argv into a GValue. Do any + * boxing/unboxing before the GValue is used to set the property. + */ +typedef void (*VipsCollectSet)( GParamSpec *pspec, GValue *value ); + +/* We need to be able to use different things to collect values for the C++ + * API: we have to box and unbox VipsImage. Set/get need to be parameters. + */ +typedef struct _VipsCollect { + VipsCollectGet get; + VipsCollectSet set; + +} VipsCollect; + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 5ebb7920..8206cf7c 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -601,7 +601,8 @@ typedef void *(*VipsObjectSetArguments)( VipsObject *object, void *a, void *b ); VipsObject *vips_object_new( GType type, VipsObjectSetArguments set, void *a, void *b ); -int vips_object_set_valist( VipsObject *object, va_list ap ); +int vips_object_set_valist( VipsObject *object, + VipsCollect *collect, va_list ap ); int vips_object_set( VipsObject *object, ... ) __attribute__((sentinel)); int vips_object_set_from_string( VipsObject *object, const char *string ); diff --git a/libvips/include/vips/operation.h b/libvips/include/vips/operation.h index 53dd63b5..b7cd6990 100644 --- a/libvips/include/vips/operation.h +++ b/libvips/include/vips/operation.h @@ -35,8 +35,6 @@ extern "C" { #endif /*__cplusplus*/ -#include - typedef enum /*< flags >*/ { VIPS_OPERATION_NONE = 0, VIPS_OPERATION_SEQUENTIAL = 1, @@ -74,20 +72,6 @@ typedef struct _VipsOperation { */ int pixels; - /* Get and set functions, used by eg. the C++ interface to box and - * unbox VImage. - */ - - /* The value has been read from the VipsOperation and written to the arg - * pointer. @arg is eg. gboolean* for a bool value. - */ - void (*collect_get)( GParamSpec *pspec, void *arg ); - - /* The value has been read from argv into a GValue. Do any - * boxing/unboxing before the GValue is used to set the property. - */ - void (*collect_set)( GParamSpec *pspec, GValue *value ); - } VipsOperation; typedef struct _VipsOperationClass { @@ -116,6 +100,8 @@ void vips_operation_invalidate( VipsOperation *operation ); int vips_operation_call_valist( VipsOperation *operation, va_list ap ); VipsOperation *vips_operation_new( const char *name ); +int vips_call_required_optional( VipsOperation **operation, + VipsCollect *collect, va_list required, va_list optional ); int vips_call( const char *operation_name, ... ) __attribute__((sentinel)); int vips_call_split( const char *operation_name, va_list optional, ... ); diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index d012c0e5..d377f284 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -2155,6 +2155,7 @@ vips_object_new( GType type, VipsObjectSetArguments set, void *a, void *b ) /** * vips_object_set_valist: * @object: object to set arguments on + * @collect: how to box or unbox values * @ap: %NULL-terminated list of argument/value pairs * * See vips_object_set(). @@ -2162,7 +2163,7 @@ vips_object_new( GType type, VipsObjectSetArguments set, void *a, void *b ) * Returns: 0 on success, -1 on error */ int -vips_object_set_valist( VipsObject *object, va_list ap ) +vips_object_set_valist( VipsObject *object, VipsCollect *collect, va_list ap ) { char *name; @@ -2181,6 +2182,9 @@ vips_object_set_valist( VipsObject *object, va_list ap ) VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap ); + if( collect ) + collect->set( pspec, &value ); + g_object_set_property( G_OBJECT( object ), name, &value ); @@ -2220,7 +2224,7 @@ vips_object_set( VipsObject *object, ... ) int result; va_start( ap, object ); - result = vips_object_set_valist( object, ap ); + result = vips_object_set_valist( object, NULL, ap ); va_end( ap ); return( result ); diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 5f8aca87..57d8ea6b 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -181,6 +181,13 @@ * compatibility only and should be hidden from users. */ +/** + * VipsCollect: + * + * We need to be able to use different things to collect values for the C++ + * API: we have to box and unbox VipsImage. Set/get need to be parameters. + */ + /* Abstract base class for operations. */ @@ -567,8 +574,8 @@ vips_operation_new( const char *name ) return( operation ); } -/* Some systems do not have va_copy() ... this might work (it does on MSVC), - * apparently. +/* Some systems do not have va_copy() ... this might work (it does on MSVC, + * apparently). * * FIXME ... this should be in configure.in */ @@ -577,7 +584,8 @@ vips_operation_new( const char *name ) #endif static int -vips_operation_set_valist_required( VipsOperation *operation, va_list ap ) +vips_operation_set_valist_required( VipsOperation *operation, + VipsCollect *collect, va_list ap ) { VIPS_DEBUG_MSG( "vips_operation_set_valist_required:\n" ); @@ -603,8 +611,8 @@ vips_operation_set_valist_required( VipsOperation *operation, va_list ap ) } #endif /*VIPS_DEBUG */ - if( operation->collect_set ) - operation->collect_set( pspec, &value ); + if( collect ) + collect->set( pspec, &value ); g_object_set_property( G_OBJECT( operation ), g_param_spec_get_name( pspec ), &value ); @@ -624,7 +632,8 @@ vips_operation_set_valist_required( VipsOperation *operation, va_list ap ) } static int -vips_operation_get_valist_required( VipsOperation *operation, va_list ap ) +vips_operation_get_valist_required( VipsOperation *operation, + VipsCollect *collect, va_list ap ) { VIPS_DEBUG_MSG( "vips_operation_get_valist_required:\n" ); @@ -672,8 +681,8 @@ vips_operation_get_valist_required( VipsOperation *operation, va_list ap ) /* Do any boxing/unboxing. */ - if( operation->collect_get ) - operation->collect_get( pspec, arg ); + if( collect ) + collect->get( pspec, arg ); VIPS_ARGUMENT_COLLECT_END } @@ -683,7 +692,8 @@ vips_operation_get_valist_required( VipsOperation *operation, va_list ap ) } static int -vips_operation_get_valist_optional( VipsOperation *operation, va_list ap ) +vips_operation_get_valist_optional( VipsOperation *operation, + VipsCollect *collect, va_list ap ) { char *name; @@ -736,8 +746,8 @@ vips_operation_get_valist_optional( VipsOperation *operation, va_list ap ) /* Do any boxing/unboxing. */ - if( operation->collect_get ) - operation->collect_get( pspec, arg ); + if( collect ) + collect->get( pspec, arg ); } VIPS_ARGUMENT_COLLECT_END @@ -746,11 +756,25 @@ vips_operation_get_valist_optional( VipsOperation *operation, va_list ap ) return( 0 ); } -/* This can change operation to point at an old, cached one. +/** + * vips_call_required_optional: + * @operation: the operation to execute + * @collect: how to box and unbox arguments + * @required: %va_list of required arguments + * @optional: NULL-terminated %va_list of name / value pairs + * + * This is the main entry point for the C and C++ varargs APIs. @operation + * is executed, supplying @required and @optional arguments. @collect is used + * to do any boxing or unboxing. It can be %NULL for no boxing on unboxing + * required (the C case). + * + * Beware, this can change @operation to point at an old, cached one. + * + * Returns: 0 on success, -1 on error */ -static int +int vips_call_required_optional( VipsOperation **operation, - va_list required, va_list optional ) + VipsCollect *collect, va_list required, va_list optional ) { int result; va_list a; @@ -762,8 +786,8 @@ vips_call_required_optional( VipsOperation **operation, */ va_copy( a, required ); va_copy( b, optional ); - result = vips_operation_set_valist_required( *operation, a ) || - vips_object_set_valist( VIPS_OBJECT( *operation ), b ); + result = vips_operation_set_valist_required( *operation, collect, a ) || + vips_object_set_valist( VIPS_OBJECT( *operation ), collect, b ); va_end( a ); va_end( b ); @@ -779,8 +803,10 @@ vips_call_required_optional( VipsOperation **operation, */ va_copy( a, required ); va_copy( b, optional ); - result = vips_operation_get_valist_required( *operation, required ) || - vips_operation_get_valist_optional( *operation, optional ); + result = vips_operation_get_valist_required( *operation, + collect, required ) || + vips_operation_get_valist_optional( *operation, + collect, optional ); va_end( a ); va_end( b ); @@ -801,7 +827,7 @@ vips_call_by_name( const char *operation_name, return( -1 ); /* Set str options before vargs options, so the user can't override - * thnigs we set deliberately. + * things we set deliberately. */ if( option_string && vips_object_set_from_string( VIPS_OBJECT( operation ), @@ -812,7 +838,8 @@ vips_call_by_name( const char *operation_name, return( -1 ); } - result = vips_call_required_optional( &operation, required, optional ); + result = vips_call_required_optional( &operation, + NULL, required, optional ); /* Build failed: junk args and back out. */