/* $Id: cpl_recipedefine.h,v 1.2 2008/02/12 09:09:51 llundin Exp $ * * This file is part of the ESO Common Pipeline Library * Copyright (C) 2001-2006 European Southern Observatory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ /* * $Author: llundin $ * $Date: 2008/02/12 09:09:51 $ * $Revision: 1.2 $ * $Name: $ */ #ifndef CPL_PLUGINDEFINE_H #define CPL_PLUGINDEFINE_H /*----------------------------------------------------------------------------- Includes -----------------------------------------------------------------------------*/ #include #include #include #include #include /* For CPL version number inconsistency */ #include /*----------------------------------------------------------------------------- Define -----------------------------------------------------------------------------*/ #ifndef CPL_CONCAT /* Needed to concatenate two macro arguments */ #define CPL_CONCAT(a,b) a ## _ ## b #define CPL_CONCAT2X(a,b) CPL_CONCAT(a,b) #endif /* A macro to generate the recipe copyright and license */ #define cpl_get_license(PACKAGE_NAME, YEAR) \ "This file is part of the " PACKAGE_NAME "\n" \ "Copyright (C) " YEAR " European Southern Observatory\n" \ "\n" \ "This program is free software; you can redistribute it and/or modify\n" \ "it under the terms of the GNU General Public License as published by\n" \ "the Free Software Foundation; either version 2 of the License, or\n" \ "(at your option) any later version.\n" \ "\n" \ "This program is distributed in the hope that it will be useful,\n" \ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ "GNU General Public License for more details.\n" \ "\n" \ "You should have received a copy of the GNU General Public License\n" \ "along with this program; if not, write to the Free Software\n" \ "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, \n" \ "MA 02111-1307 USA" /*----------------------------------------------------------------------------*/ /** @brief Define a standard CPL recipe @param RECIPE_NAME The name as an identifier @param RECIPE_VERSION The binary version number @param RECIPE_FILL_PARAMS A function call to fill the recipe parameterlist. Must evaluate to zero if and only if successful @param RECIPE_AUTHOR The author as a string literal @param RECIPE_YEAR The copyright year as a string literal @param RECIPE_SYNOPSIS The synopsis as a string literal @param RECIPE_DESCRIPTION The man-page as a string literal A CPL-based recipe may use this macro to define its four mandatory functions: cpl_plugin_get_info(), _create(), _exec() and _destroy(), as well as declaring the actual data reduction function, () as @code static int (cpl_frameset *, const cpl_parameterlist *); @endcode This may be done by defining a macro, e.g. in my_recipe.h: @code #define MY_RECIPE_DEFINE(NAME, FILL_PARAMS_FUNC, SYNOPSIS, DESCRIPTION) \ CPL_RECIPE_DEFINE(NAME, MY_BINARY_VERSION, \ FILL_PARAMS_FUNC(recipe->parameters), \ "Firstname Lastname", "2006, 2008", SYNOPSIS, DESCRIPTION) \ @endcode - and then by invoking this macro in each recipe: @code #include "my_recipe.h" MY_RECIPE_DEFINE(instrume_img_dark, instrume_img_dark_fill_parameterlist, "Dark recipe", "instrume_img_dark -- imaging dark recipe.\n" " ... recipe man-page\n") @endcode where instrume_img_dark_fill_parameterlist() is a recipe specific function, that takes an empty parameterlist and fills it with those parameters that the recipe supports. */ /*----------------------------------------------------------------------------*/ #define CPL_RECIPE_DEFINE(RECIPE_NAME, RECIPE_VERSION, RECIPE_FILL_PARAMS, \ RECIPE_AUTHOR, RECIPE_AUTHOR_EMAIL, RECIPE_YEAR, \ RECIPE_SYNOPSIS, RECIPE_DESCRIPTION) \ \ /* The prototypes of the recipe create, exec and destroy functions */ \ static int CPL_CONCAT2X(RECIPE_NAME,create) (cpl_plugin * plugin); \ static int CPL_CONCAT2X(RECIPE_NAME,exec) (cpl_plugin * plugin); \ static int CPL_CONCAT2X(RECIPE_NAME,destroy)(cpl_plugin * plugin); \ \ /* The prototype of the function called by the recipe exec function */ \ static int RECIPE_NAME(cpl_frameset *, const cpl_parameterlist *); \ \ int cpl_plugin_get_info(cpl_pluginlist * list) \ { \ cpl_recipe * recipe; \ cpl_plugin * plugin; \ /* Verify that the compile-time and run-time versions of CPL match */ \ /* - this will work for run-time versions going back to CPL 3.0 */ \ /* - earlier versions will cause an exit with an unresolved symbol */ \ const unsigned vruntime = CPL_VERSION(cpl_version_get_major(), \ cpl_version_get_minor(), \ cpl_version_get_micro()); \ /* The version number of the first major version */ \ const unsigned vmruntime = CPL_VERSION(cpl_version_get_major(), 0, 0); \ \ \ if (vruntime < CPL_VERSION_CODE) { \ cpl_msg_warning(cpl_func, "Run-time version %s of CPL is lower than " \ "the compile-time version (%X)", \ cpl_version_get_version(), CPL_VERSION_CODE); \ } else if (vmruntime > CPL_VERSION_CODE) { \ cpl_msg_warning(cpl_func, "Run-time version %s of CPL has a newer " \ "major version than the compile-time version (%X)", \ cpl_version_get_version(), CPL_VERSION_CODE); \ } else if (vruntime > CPL_VERSION_CODE) { \ cpl_msg_info(cpl_func, "Run-time version %s of CPL is higher than " \ "the compile-time version (%X)", \ cpl_version_get_version(), CPL_VERSION_CODE); \ } \ \ recipe = cpl_calloc(1, sizeof(*recipe)); \ if (recipe == NULL) { \ (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT, \ "Recipe allocation failed"); \ return 1; \ } \ \ plugin = &recipe->interface; \ \ if (cpl_plugin_init(plugin, \ CPL_PLUGIN_API, \ RECIPE_VERSION, \ CPL_PLUGIN_TYPE_RECIPE, \ #RECIPE_NAME, \ RECIPE_SYNOPSIS, \ RECIPE_DESCRIPTION, \ RECIPE_AUTHOR, \ RECIPE_AUTHOR_EMAIL, \ cpl_get_license(PACKAGE_NAME, RECIPE_YEAR), \ CPL_CONCAT2X(RECIPE_NAME,create), \ CPL_CONCAT2X(RECIPE_NAME,exec), \ CPL_CONCAT2X(RECIPE_NAME,destroy))) { \ (void)cpl_error_set_message(cpl_func, cpl_error_get_code(), \ "Plugin initialization failed"); \ return 1; \ } \ \ if (cpl_pluginlist_append(list, plugin)) { \ (void)cpl_error_set_message(cpl_func, cpl_error_get_code(), \ "Error adding plugin to list"); \ return 1; \ } \ \ return 0; \ } \ \ /* The definition of the recipe create function */ \ static int CPL_CONCAT2X(RECIPE_NAME,create)(cpl_plugin * plugin) \ { \ cpl_recipe * recipe; \ \ /* Do not create the recipe if an error code is already set */ \ if (cpl_error_get_code() != CPL_ERROR_NONE) { \ return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(), \ "An error is already set"); \ } \ \ if (plugin == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, \ "Null plugin"); \ } \ \ /* Verify plugin type */ \ if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_TYPE_MISMATCH, \ "Plugin is not a recipe"); \ } \ \ /* Get the recipe */ \ recipe = (cpl_recipe *)plugin; \ \ /* Create the parameters list in the cpl_recipe object */ \ recipe->parameters = cpl_parameterlist_new(); \ if (recipe->parameters == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT, \ "Parameter list allocation failed"); \ } \ \ /* Fill the parameters list */ \ return(RECIPE_FILL_PARAMS); \ } \ \ /* The definition of the recipe exec function */ \ static int CPL_CONCAT2X(RECIPE_NAME,exec)(cpl_plugin * plugin) \ { \ cpl_recipe * recipe; \ int recipe_status; \ cpl_errorstate initial_errorstate = cpl_errorstate_get(); \ \ /* Return immediately if an error code is already set */ \ if (cpl_error_get_code() != CPL_ERROR_NONE) { \ return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(), \ "An error is already set"); \ } \ \ if (plugin == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, \ "Null plugin"); \ } \ \ /* Verify plugin type */ \ if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_TYPE_MISMATCH, \ "Plugin is not a recipe"); \ } \ \ /* Get the recipe */ \ recipe = (cpl_recipe *)plugin; \ \ /* Verify parameter and frame lists */ \ if (recipe->parameters == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, \ "Recipe invoked with NULL " \ "parameter list"); \ } \ if (recipe->frames == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, \ "Recipe invoked with NULL " \ "frame set"); \ } \ \ /* Invoke the recipe */ \ recipe_status = RECIPE_NAME(recipe->frames, recipe->parameters); \ \ if (!cpl_errorstate_is_equal(initial_errorstate)) { \ /* Dump the error history since recipe execution start. \ At this point the recipe cannot recover from the error */ \ cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL); \ } \ \ return recipe_status; \ } \ \ /* The definition of the recipe destroy function */ \ static int CPL_CONCAT2X(RECIPE_NAME,destroy)(cpl_plugin * plugin) \ { \ cpl_recipe * recipe; \ \ if (plugin == NULL) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, \ "Null plugin"); \ } \ \ /* Verify plugin type */ \ if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) { \ return (int)cpl_error_set_message(cpl_func, CPL_ERROR_TYPE_MISMATCH, \ "Plugin is not a recipe"); \ } \ \ /* Get the recipe */ \ recipe = (cpl_recipe *)plugin; \ \ cpl_parameterlist_delete(recipe->parameters); /* May be NULL */ \ \ return 0; \ } \ \ /* This dummy declaration requires the macro to be invoked as if it was \ a kind of function definition, with a terminating ; */ \ extern int cpl_plugin_end #endif