diff options
author | Kjell Ahlstedt <kjell.ahlstedt@bredband.net> | 2011-02-14 16:24:27 +0100 |
---|---|---|
committer | Murray Cumming <murrayc@murrayc.com> | 2011-02-15 12:41:59 +0100 |
commit | 94a1f3d568decbde9c3fab84c34db5741ffcad62 (patch) | |
tree | 7b25ba17b002e06ea7d8642a4ebe61cfe3038be9 /glib/src/optiongroup.ccg | |
parent | efa5be64159016d2b9cc4ac8d1cc5a6176703ce7 (diff) | |
download | glibmm-94a1f3d568decbde9c3fab84c34db5741ffcad62.tar.gz |
OptionGroup: Add add_entry() that takes a slot with callback function.
* glib/src/optiongroup.[hg|ccg]: Add add_entry() and add_entry_filename()
that take a slot. Add protected option_arg_callback().
An exception thrown by on_pre_parse() or on_post_parse() is propagated
to the error argument of g_callback_pre_parse() or post_parse_callback().
* glib/src/optionentry.hg: Add description of set_flags().
* examples/options/main.cc: Add more OptionEntries and callback functions
for parsing command option values.
Bug 589197 (Hubert Figuiere)
Diffstat (limited to 'glib/src/optiongroup.ccg')
-rw-r--r-- | glib/src/optiongroup.ccg | 273 |
1 files changed, 242 insertions, 31 deletions
diff --git a/glib/src/optiongroup.ccg b/glib/src/optiongroup.ccg index ed5e45a6..358d8661 100644 --- a/glib/src/optiongroup.ccg +++ b/glib/src/optiongroup.ccg @@ -31,25 +31,79 @@ namespace Glib namespace //anonymous { +//A pointer to an OptionArgCallback instance is stored in CppOptionEntry::cpparg_ +//when a callback function shall parse the command option's value. +class OptionArgCallback +{ +public: + OptionArgCallback(const OptionGroup::SlotOptionArgString& slot) + : slot_string(new OptionGroup::SlotOptionArgString(slot)), slot_filename(0) + { } + + OptionArgCallback(const OptionGroup::SlotOptionArgFilename& slot) + : slot_string(0), slot_filename(new OptionGroup::SlotOptionArgFilename(slot)) + { } + + bool is_filename_option() const { return slot_filename != 0; } + const OptionGroup::SlotOptionArgString* get_slot_string() const { return slot_string; } + const OptionGroup::SlotOptionArgFilename* get_slot_filename() const { return slot_filename; } + + ~OptionArgCallback() + { + delete slot_string; + delete slot_filename; + } + +private: + //One of these slot pointers is 0 and the other one points to a slot. + OptionGroup::SlotOptionArgString* slot_string; + OptionGroup::SlotOptionArgFilename* slot_filename; + + //Not copyable + OptionArgCallback(const OptionArgCallback&); + OptionArgCallback& operator=(const OptionArgCallback&); +}; + extern "C" { -static gboolean g_callback_pre_parse(GOptionContext* context, GOptionGroup* /* group */, gpointer data, GError** /* TODO error */) +static gboolean g_callback_pre_parse(GOptionContext* context, + GOptionGroup* /* group */, gpointer data, GError** error) { OptionContext cppContext(context, false /* take_ownership */); - //OptionGroup cppGroup(group, true /* take_copy */); //Maybe this should be option_group. OptionGroup* option_group = static_cast<OptionGroup*>(data); - if(option_group) - return option_group->on_pre_parse(cppContext, *option_group); - else + if(!option_group) + { + OptionError(OptionError::FAILED, "Glib::OptionGroup: g_callback_pre_parse(): " + "No OptionGroup pointer available").propagate(error); return false; + } + + try + { + return option_group->on_pre_parse(cppContext, *option_group); + } + catch(Glib::Error& err) + { + err.propagate(error); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return false; } -static void g_callback_error(GOptionContext* context, GOptionGroup* /* group */, gpointer data, GError** /* TODO error*/) +static void g_callback_error(GOptionContext* context, + GOptionGroup* /* group */, gpointer data, GError** /* TODO error */) { + // TODO GError** error is input data containing information on an error that + // has occurred before this function is called. When API can be broken, + // the function prototype of on_error ought to be changed to + // void on_error(OptionContext& context, Error& error). + OptionContext cppContext(context, false /* take_ownership */); - //OptionGroup cppGroup(group); //Maybe this should be option_group. OptionGroup* option_group = static_cast<OptionGroup*>(data); if(option_group) @@ -86,33 +140,133 @@ static void OptionGroup_Translate_glibmm_callback_destroy(void* data) //static gboolean OptionGroup::post_parse_callback(GOptionContext* context, - GOptionGroup* /* group */, gpointer data, GError** /* TODO error */) + GOptionGroup* /* group */, gpointer data, GError** error) { OptionContext cppContext(context, false /* take_ownership */); - //OptionGroup cppGroup(group, true /* take_copy */); //Maybe this should be option_group. OptionGroup* option_group = static_cast<OptionGroup*>(data); - if(option_group) + if(!option_group) { - //The C args have now been given values by g_option_context_parse(). - //Convert C values to C++ values: + OptionError(OptionError::FAILED, "Glib::OptionGroup::post_parse_callback(): " + "No OptionGroup pointer available").propagate(error); + return false; + } - for(type_map_entries::iterator iter = option_group->map_entries_.begin(); - iter != option_group->map_entries_.end(); ++iter) - { - CppOptionEntry& cpp_entry = iter->second; - cpp_entry.convert_c_to_cpp(); - } + //The C args have now been given values by g_option_context_parse(). + //Convert C values to C++ values: + for(type_map_entries::iterator iter = option_group->map_entries_.begin(); + iter != option_group->map_entries_.end(); ++iter) + { + CppOptionEntry& cpp_entry = iter->second; + cpp_entry.convert_c_to_cpp(); + } + + try + { return option_group->on_post_parse(cppContext, *option_group); } + catch(Glib::Error& err) + { + err.propagate(error); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return false; +} + +//static +gboolean OptionGroup::option_arg_callback(const gchar* option_name, const gchar* value, + gpointer data, GError** error) +{ + const Glib::ustring cpp_option_name(option_name); + const OptionGroup* const option_group = static_cast<const OptionGroup*>(data); + if(!option_group) + { + OptionError(OptionError::FAILED, "Glib::OptionGroup::option_arg_callback(): " + "No OptionGroup pointer available for option " + cpp_option_name).propagate(error); + return false; + } + + //option_name is either a single dash followed by a single letter (for a + //short name) or two dashes followed by a long option name. + OptionGroup::type_map_entries::const_iterator iterFind = option_group->map_entries_.end(); + if(option_name[1] == '-') + { + //Long option name. + const Glib::ustring long_option_name = Glib::ustring(option_name+2); + iterFind = option_group->map_entries_.find(long_option_name); + } else + { + //Short option name. + const gchar short_option_name = option_name[1]; + for(iterFind = option_group->map_entries_.begin(); + iterFind != option_group->map_entries_.end(); ++iterFind) + { + const OptionGroup::CppOptionEntry& cppOptionEntry = iterFind->second; + if (cppOptionEntry.entry_ && + cppOptionEntry.entry_->get_short_name() == short_option_name) + break; + } + } + + if(iterFind == option_group->map_entries_.end()) + { + OptionError(OptionError::UNKNOWN_OPTION, "Glib::OptionGroup::option_arg_callback(): " + "Unknown option " + cpp_option_name).propagate(error); + return false; + } + + const OptionGroup::CppOptionEntry& cppOptionEntry = iterFind->second; + if (cppOptionEntry.carg_type_ != G_OPTION_ARG_CALLBACK) + { + OptionError(OptionError::FAILED, "Glib::OptionGroup::option_arg_callback() " + "called for non-callback option " + cpp_option_name).propagate(error); return false; + } + + const bool has_value = (value != 0); + const OptionArgCallback* const option_arg = + static_cast<const OptionArgCallback*>(cppOptionEntry.cpparg_); + try + { + if (option_arg->is_filename_option()) + { + const OptionGroup::SlotOptionArgFilename* the_slot = option_arg->get_slot_filename(); + const std::string cpp_value(value ? value : ""); + return (*the_slot)(cpp_option_name, cpp_value, has_value); + } + else + { + const OptionGroup::SlotOptionArgString* the_slot = option_arg->get_slot_string(); + const Glib::ustring cpp_value(value ? value : ""); + return (*the_slot)(cpp_option_name, cpp_value, has_value); + } + } + catch(Glib::Error& err) + { + err.propagate(error); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return false; } OptionGroup::OptionGroup(const Glib::ustring& name, const Glib::ustring& description, const Glib::ustring& help_description) -: gobject_( g_option_group_new(name.c_str(), description.c_str(), help_description.c_str(), this, 0 /* destroy_func */) ), +: gobject_( g_option_group_new(name.c_str(), description.c_str(), help_description.c_str(), + this /* user_data */, 0 /* destroy_func */) ), has_ownership_(true) { + //g_callback_pre_parse(), post_parse_callback(), g_callback_error(), and + //option_arg_callback() depend on user_data being this. The first three + //functions get a GOptionGroup*, but it would not be correct to use it for + //creating a new OptionGroup. They must call their virtual functions in the + //original OptionGroup instance. + //Connect callbacks, so that derived classes can override the virtual methods: g_option_group_set_parse_hooks(gobj(), &g_callback_pre_parse, &post_parse_callback); g_option_group_set_error_hook(gobj(), &g_callback_error); @@ -191,7 +345,30 @@ void OptionGroup::add_entry_filename(const OptionEntry& entry, vecstrings& arg) { add_entry_with_wrapper(entry, G_OPTION_ARG_FILENAME_ARRAY, &arg); } - + +// When the command argument value is to be parsed by a user-supplied function +// (indicated by G_OPTION_ARG_CALLBACK), the FLAG_FILENAME in 'entry' is ignored. +// set_c_arg_default() clears or sets it as required in a copy of 'entry'. +// +// The glib API is inconsistent here. The choice between UTF-8 and filename +// encoding is done with G_OPTION_ARG_STRING, G_OPTION_ARG_FILENAME, +// G_OPTION_ARG_STRING_ARRAY, and G_OPTION_ARG_FILENAME_ARRAY, which in glibmm +// are set by OptionGroup::add_entry[_filename]. But when a callback function +// is chosen, there is only G_OPTION_ARG_CALLBACK, and the encoding is chosen +// with G_OPTION_FLAG_FILENAME. Other option flags are set by OptionEntry::set_flags(). + +void OptionGroup::add_entry(const OptionEntry& entry, const SlotOptionArgString& slot) +{ + //The OptionArgCallback is deleted in release_c_arg(). + add_entry_with_wrapper(entry, G_OPTION_ARG_CALLBACK, new OptionArgCallback(slot)); +} + +void OptionGroup::add_entry_filename(const OptionEntry& entry, const SlotOptionArgFilename& slot) +{ + //The OptionArgCallback is deleted in release_c_arg(). + add_entry_with_wrapper(entry, G_OPTION_ARG_CALLBACK, new OptionArgCallback(slot)); +} + void OptionGroup::add_entry_with_wrapper(const OptionEntry& entry, GOptionArg arg_type, void* cpp_arg) { const Glib::ustring name = entry.get_long_name(); @@ -199,6 +376,10 @@ void OptionGroup::add_entry_with_wrapper(const OptionEntry& entry, GOptionArg ar if( iterFind == map_entries_.end() ) //If we have not added this entry already { CppOptionEntry cppEntry; + //g_option_group_add_entry() does not take its own copy, so we must keep the instance alive. + cppEntry.entry_ = new OptionEntry(entry); + //cppEntry.entry_ is deleted in release_c_arg(), via the destructor. + cppEntry.carg_type_ = arg_type; cppEntry.allocate_c_arg(); cppEntry.set_c_arg_default(cpp_arg); @@ -206,10 +387,6 @@ void OptionGroup::add_entry_with_wrapper(const OptionEntry& entry, GOptionArg ar cppEntry.cpparg_ = cpp_arg; //Give the information to the C API: - - cppEntry.entry_ = new OptionEntry(entry); //g_option_group_add_entry() does not take its own copy, so we must keep the instance alive. */ - //cppEntry.entry_ is deleted in release_c_arg(), via the destructor. - cppEntry.entry_->gobj()->arg = arg_type; cppEntry.entry_->gobj()->arg_data = cppEntry.carg_; @@ -256,7 +433,8 @@ void OptionGroup::CppOptionEntry::allocate_c_arg() //Create an instance of the appropriate C type. //This will be destroyed in the OptionGroup destructor. // - //We must also call set_c_arg_default() to give these C types the specified defaults based on the C++-typed arguments. + //We must also call set_c_arg_default() to give these C types the specified + //defaults based on the C++-typed arguments. switch(carg_type_) { case G_OPTION_ARG_STRING: //The char* will be for UTF8 strins. @@ -303,6 +481,13 @@ void OptionGroup::CppOptionEntry::allocate_c_arg() break; } + case G_OPTION_ARG_CALLBACK: + { + //The C arg pointer is a function pointer. + carg_ = reinterpret_cast<void*>(&OptionGroup::option_arg_callback); + + break; + } default: { break; @@ -389,6 +574,22 @@ void OptionGroup::CppOptionEntry::set_c_arg_default(void* cpp_arg) } break; } + case G_OPTION_ARG_CALLBACK: + { + //No value to set here. The arg pointer is a function pointer. + + //Set or clear FLAG_FILENAME in *entry_. + const OptionArgCallback* const option_arg = static_cast<const OptionArgCallback*>(cpp_arg); + if (option_arg->is_filename_option()) + { + entry_->set_flags(entry_->get_flags() | OptionEntry::FLAG_FILENAME); + } + else + { + entry_->set_flags(entry_->get_flags() & ~OptionEntry::FLAG_FILENAME); + } + break; + } default: { break; @@ -440,11 +641,18 @@ void OptionGroup::CppOptionEntry::release_c_arg() break; } + case G_OPTION_ARG_CALLBACK: + { + //Delete the OptionArgCallback instance that was allocated by add_entry() + //or add_entry_filename(). + OptionArgCallback* option_arg = static_cast<OptionArgCallback*>(cpparg_); + delete option_arg; + cpparg_ = 0; + + break; + } default: { - /* TODO: - G_OPTION_ARG_CALLBACK, - */ break; } } @@ -560,11 +768,14 @@ void OptionGroup::CppOptionEntry::convert_c_to_cpp() *(static_cast<bool*>(cpparg_)) = *(static_cast<gboolean*>(carg_)); break; } + case G_OPTION_ARG_CALLBACK: + { + //Nothing to convert here. That's a task for the callback function + //(the SlotOptionArgString or SlotOptionArgFilename). + break; + } default: { - /* TODO: - G_OPTION_ARG_CALLBACK, - */ break; } } |