more cplusplus hackery

This commit is contained in:
John Cupitt 2014-10-21 14:55:38 +01:00
parent 904803510d
commit 952241a426
10 changed files with 237 additions and 66 deletions

View File

@ -37,10 +37,11 @@
#include <cstdio>
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>
#include <vips/vipscpp.h>
#include <gobject/gvaluecollector.h>
#include "include/vips/vips8"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
@ -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<VImage *>(
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 );
}

View File

@ -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

View File

@ -1,3 +1,3 @@
VImage add( VImage add_in2, ... )
__attribute__((sentinel)) throw( VError );
throw( VError ) __attribute__((sentinel));

View File

@ -37,7 +37,7 @@
#define VIPS_NAMESPACE_START namespace vips8 {
#define VIPS_NAMESPACE_END }
#include <vips/VError.h>
#include <vips/VImage.h>
#include "VError.h"
#include "VImage.h"
#endif /*VIPS_CPLUSPLUS*/

View File

@ -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 );

View File

@ -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*/

View File

@ -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 );

View File

@ -35,8 +35,6 @@
extern "C" {
#endif /*__cplusplus*/
#include <vips/vips.h>
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, ... );

View File

@ -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 );

View File

@ -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.
*/