source: 3DVCSoftware/trunk/source/Lib/TAppCommon/program_options_lite.h @ 1356

Last change on this file since 1356 was 1356, checked in by tech, 9 years ago

Merged 15.1-dev0-NICT@1355.

File size: 21.9 KB
Line 
1/* The copyright in this software is being made available under the BSD
2 * License, included below. This software may be subject to other third party
3 * and contributor rights, including patent rights, and no such rights are
4 * granted under this license.
5 *
6 * Copyright (c) 2010-2015, ITU/ISO/IEC
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 *  * Redistributions of source code must retain the above copyright notice,
13 *    this list of conditions and the following disclaimer.
14 *  * Redistributions in binary form must reproduce the above copyright notice,
15 *    this list of conditions and the following disclaimer in the documentation
16 *    and/or other materials provided with the distribution.
17 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
18 *    be used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 */
33#include <iostream>
34#include <sstream>
35#include <string>
36#include <list>
37#include <map>
38#include  "../TLibCommon/CommonDef.h"
39
40#if NH_MV
41#include <vector>
42#include <errno.h>
43#include <cstring>
44
45#ifdef WIN32
46#define strdup _strdup
47#endif
48#endif
49
50
51#ifndef __PROGRAM_OPTIONS_LITE__
52#define __PROGRAM_OPTIONS_LITE__
53
54//! \ingroup TAppCommon
55//! \{
56
57
58namespace df
59{
60  namespace program_options_lite
61  {
62    struct Options;
63
64    struct ParseFailure : public std::exception
65    {
66      ParseFailure(std::string arg0, std::string val0) throw()
67      : arg(arg0), val(val0)
68      {}
69
70      ~ParseFailure() throw() {};
71
72      std::string arg;
73      std::string val;
74
75      const char* what() const throw() { return "Option Parse Failure"; }
76    };
77
78    struct ErrorReporter
79    {
80#if NH_MV_SEI
81      ErrorReporter() : is_errored(0), output_on_unknow_parameter(true)  {}
82#else
83      ErrorReporter() : is_errored(0) {}
84#endif
85      virtual ~ErrorReporter() {}
86      virtual std::ostream& error(const std::string& where);
87      virtual std::ostream& warn(const std::string& where);
88      bool is_errored;
89#if NH_MV_SEI
90      bool output_on_unknow_parameter;
91#endif
92    };
93
94    extern ErrorReporter default_error_reporter;
95
96    void doHelp(std::ostream& out, Options& opts, unsigned columns = 80);
97    std::list<const char*> scanArgv(Options& opts, unsigned argc, const char* argv[], ErrorReporter& error_reporter = default_error_reporter);
98    void setDefaults(Options& opts);
99    void parseConfigFile(Options& opts, const std::string& filename, ErrorReporter& error_reporter = default_error_reporter);
100
101    /** OptionBase: Virtual base class for storing information relating to a
102     * specific option This base class describes common elements.  Type specific
103     * information should be stored in a derived class. */
104    struct OptionBase
105    {
106#if NH_MV     
107#if NH_MV_SEI     
108      OptionBase(const std::string& name, const std::string& desc, bool duplicate = false, std::vector< int > maxdim = std::vector< int >(0) )
109        : opt_string(name), opt_desc(desc), opt_duplicate(duplicate), max_dim( maxdim )
110#else
111      OptionBase(const std::string& name, const std::string& desc, bool duplicate = false)
112        : opt_string(name), opt_desc(desc), opt_duplicate(duplicate)
113#endif
114#else
115      OptionBase(const std::string& name, const std::string& desc)
116      : opt_string(name), opt_desc(desc)
117#endif
118      {};
119
120      virtual ~OptionBase() {}
121
122      /* parse argument arg, to obtain a value for the option */
123#if NH_MV_SEI
124      virtual void parse(const std::string& arg, const std::vector<int>& idcs,  ErrorReporter&) = 0;
125     
126      bool   checkDim( std::vector< int > dims, ErrorReporter& err )
127      {     
128        bool doParsing = true; 
129        if ( dims.size() != max_dim.size() )
130        {
131            err.error(" ") << "Number of indices of `" <<  opt_string << "' not matching. Should be " << max_dim.size() << std::endl; 
132            doParsing = false; 
133        }
134
135        for (size_t i = 0 ; i < dims.size(); i++ )
136        {
137          if ( dims[i] >= max_dim[i] )
138          {
139            if (err.output_on_unknow_parameter )
140            {       
141              err.warn(" ") << "Index " << i  << " of  " <<  opt_string << " should be less than " << max_dim[i] << std::endl;             
142              doParsing = false; 
143            }
144          }
145        }
146        return doParsing; 
147      }
148
149      void   xParseVec( const std::string& arg, BoolAry1d& storage )
150      {       
151        char* pcNextStart = (char*) arg.data();
152        char* pcEnd = pcNextStart + arg.length();
153
154        char* pcOldStart = 0; 
155
156        size_t iIdx = 0; 
157
158        while (pcNextStart < pcEnd)
159        {
160          if ( iIdx < storage.size() )
161          {
162            storage[iIdx] = (strtol(pcNextStart, &pcNextStart,10) != 0);
163          }
164          else
165          {
166            storage.push_back(strtol(pcNextStart, &pcNextStart,10) != 0) ;
167          }
168          iIdx++; 
169
170          if ( errno == ERANGE || (pcNextStart == pcOldStart) )
171          {
172            std::cerr << "Error Parsing Bools: `" << arg << "'" << std::endl;
173            exit(EXIT_FAILURE);
174          };   
175          while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
176          pcOldStart = pcNextStart;
177        }
178      }
179
180      void   xParseVec( const std::string& arg, IntAry1d& storage )
181      {       
182        storage.clear();
183
184        char* pcNextStart = (char*) arg.data();
185        char* pcEnd = pcNextStart + arg.length();
186
187        char* pcOldStart = 0; 
188
189        size_t iIdx = 0; 
190
191
192        while (pcNextStart < pcEnd)
193        {
194
195          if ( iIdx < storage.size() )
196          {
197            storage[iIdx] = (int) strtol(pcNextStart, &pcNextStart,10);
198          }
199          else
200          {
201            storage.push_back( (int) strtol(pcNextStart, &pcNextStart,10)) ;
202          }
203          iIdx++; 
204          if ( errno == ERANGE || (pcNextStart == pcOldStart) )
205          {
206            std::cerr << "Error Parsing Integers: `" << arg << "'" << std::endl;
207            exit(EXIT_FAILURE);
208          };   
209          while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
210          pcOldStart = pcNextStart;
211        }     
212      }
213#else
214      virtual void parse(const std::string& arg, ErrorReporter&) = 0;
215#endif
216      /* set the argument to the default value */
217      virtual void setDefault() = 0;
218
219      std::string opt_string;
220      std::string opt_desc;
221#if NH_MV
222      bool        opt_duplicate; 
223#if NH_MV_SEI
224      std::vector<int> max_dim;
225#endif
226#endif
227    };
228
229    /** Type specific option storage */
230    template<typename T>
231    struct Option : public OptionBase
232    {
233#if NH_MV
234#if NH_MV_SEI
235      Option(const std::string& name, T& storage, T default_val, const std::string& desc, bool duplicate = false, std::vector< int > maxdim = std::vector< int >(0) )
236        : OptionBase(name, desc, duplicate, maxdim), opt_storage(storage), opt_default_val(default_val)
237#else
238      Option(const std::string& name, T& storage, T default_val, const std::string& desc, bool duplicate = false)
239        : OptionBase(name, desc, duplicate), opt_storage(storage), opt_default_val(default_val)
240#endif
241#else
242      Option(const std::string& name, T& storage, T default_val, const std::string& desc)
243      : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val)
244#endif
245      {}
246
247#if NH_MV_SEI
248      void parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter&);
249#else
250      void parse(const std::string& arg, ErrorReporter&);
251#endif
252
253      void setDefault()
254      {
255        opt_storage = opt_default_val;
256      }
257
258      T& opt_storage;
259      T opt_default_val;
260    };
261
262    /* Generic parsing */
263    template<typename T>
264    inline void
265#if NH_MV_SEI
266    Option<T>::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter&)
267#else
268    Option<T>::parse(const std::string& arg, ErrorReporter&)
269#endif
270    {
271#if NH_MV_SEI
272      assert( idcs.size() == 0 ); 
273#endif
274     
275      std::istringstream arg_ss (arg,std::istringstream::in);
276      arg_ss.exceptions(std::ios::failbit);
277      try
278      {
279        arg_ss >> opt_storage;
280      }
281      catch (...)
282      {
283        throw ParseFailure(opt_string, arg);
284      }
285    }
286
287    /* string parsing is specialized -- copy the whole string, not just the
288     * first word */
289    template<>
290    inline void
291#if NH_MV_SEI
292    Option<std::string>::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter&)
293#else
294    Option<std::string>::parse(const std::string& arg, ErrorReporter&)
295#endif
296    {
297#if NH_MV_SEI
298      assert( idcs.size() == 0 ); 
299#endif
300      opt_storage = arg;
301    }
302
303#if NH_MV   
304    template<>
305    inline void
306#if NH_MV_SEI
307      Option<char*>::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter&)
308#else
309      Option<char*>::parse(const std::string& arg, ErrorReporter&)
310#endif
311    {
312#if NH_MV_SEI
313      assert( idcs.size() == 0 ); 
314#endif
315      opt_storage = arg.empty() ? NULL : strdup(arg.c_str()) ;
316    }
317
318#if !NH_MV_SEI
319
320    template<>
321    inline void
322      Option< std::vector<char*> >::parse(const std::string& arg, ErrorReporter&)
323    {
324      opt_storage.clear(); 
325
326      char* pcStart = (char*) arg.data();     
327      char* pcEnd = strtok (pcStart," ");
328
329      while (pcEnd != NULL)
330      {
331        size_t uiStringLength = pcEnd - pcStart;
332        char* pcNewStr = (char*) malloc( uiStringLength + 1 );
333        strncpy( pcNewStr, pcStart, uiStringLength); 
334        pcNewStr[uiStringLength] = '\0'; 
335        pcStart = pcEnd+1; 
336        pcEnd = strtok (NULL, " ,.-");
337        opt_storage.push_back( pcNewStr ); 
338      }     
339    }
340#endif
341
342    template<>   
343    inline void
344#if NH_MV_SEI
345      Option< std::vector<double> >::parse(const std::string& arg, const std::vector< int > & idcs, ErrorReporter&)
346#else
347      Option< std::vector<double> >::parse(const std::string& arg, ErrorReporter&)
348#endif
349    {
350#if NH_MV_SEI
351      assert( idcs.size() == 0 ); 
352#endif
353      char* pcNextStart = (char*) arg.data();
354      char* pcEnd = pcNextStart + arg.length();
355
356      char* pcOldStart = 0; 
357
358      size_t iIdx = 0; 
359
360      while (pcNextStart < pcEnd)
361      {
362        errno = 0; 
363
364        if ( iIdx < opt_storage.size() )
365        {
366          opt_storage[iIdx] = strtod(pcNextStart, &pcNextStart);
367        }
368        else
369        {
370          opt_storage.push_back( strtod(pcNextStart, &pcNextStart)) ;
371        }
372        iIdx++; 
373
374        if ( errno == ERANGE || (pcNextStart == pcOldStart) )
375        {
376          std::cerr << "Error Parsing Doubles: `" << arg << "'" << std::endl;
377          exit(EXIT_FAILURE);   
378        };   
379        while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
380        pcOldStart = pcNextStart; 
381
382      }
383    }
384
385
386#if NH_MV_SEI
387    template<>
388    inline void
389      Option< IntAry1d >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter& err)
390    {
391      xParseVec( arg, opt_storage );
392    };
393
394    template<>
395    inline void
396      Option< IntAry2d >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter&)
397    {
398      xParseVec( arg, opt_storage[ idcs[0] ] );
399    };
400
401    template<>
402    inline void
403      Option< IntAry3d >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter&)
404    {
405      xParseVec ( arg, opt_storage[ idcs[0] ][ idcs[1] ] );
406    };
407#if SEI_DRI_F0169
408    template<>
409    inline void
410    Option< std::vector< std::vector<double> > >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter&)
411    {
412        // xParseVec ( arg, opt_storage[ idcs[0] ] );
413        char* pcNextStart = (char*) arg.data();
414        char* pcEnd = pcNextStart + arg.length();
415
416        char* pcOldStart = 0; 
417
418        size_t iIdx = 0; 
419
420        while (pcNextStart < pcEnd)
421        {
422            errno = 0; 
423
424            if ( iIdx < opt_storage[idcs[0]].size() )
425            {
426                opt_storage[idcs[0]][iIdx] = strtod(pcNextStart, &pcNextStart);
427            }
428            else
429            {
430                opt_storage[idcs[0]].push_back( strtod(pcNextStart, &pcNextStart)) ;
431            }
432            iIdx++; 
433
434            if ( errno == ERANGE || (pcNextStart == pcOldStart) )
435            {
436                std::cerr << "Error Parsing Doubles: `" << arg << "'" << std::endl;
437                exit(EXIT_FAILURE);   
438            };   
439            while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
440            pcOldStart = pcNextStart; 
441
442        }       
443
444
445    }
446#endif
447#else
448    template<>
449    inline void
450      Option< std::vector<int> >::parse(const std::string& arg, ErrorReporter&)
451    {
452      opt_storage.clear();
453
454
455      char* pcNextStart = (char*) arg.data();
456      char* pcEnd = pcNextStart + arg.length();
457
458      char* pcOldStart = 0; 
459
460      size_t iIdx = 0; 
461
462
463      while (pcNextStart < pcEnd)
464      {
465
466        if ( iIdx < opt_storage.size() )
467        {
468          opt_storage[iIdx] = (int) strtol(pcNextStart, &pcNextStart,10);
469        }
470        else
471        {
472          opt_storage.push_back( (int) strtol(pcNextStart, &pcNextStart,10)) ;
473        }
474        iIdx++; 
475        if ( errno == ERANGE || (pcNextStart == pcOldStart) )
476        {
477          std::cerr << "Error Parsing Integers: `" << arg << "'" << std::endl;
478          exit(EXIT_FAILURE);
479        };   
480        while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
481        pcOldStart = pcNextStart;
482      }
483    }
484#endif
485
486#if NH_MV_SEI
487
488    template<>
489    inline void
490      Option< StringAry1d  >::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter& err )
491    {
492
493      opt_storage[ idcs[ 0 ] ] = arg;
494    };
495
496    template<>
497    inline void
498      Option< StringAry2d  >::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter& err )
499    {
500
501      opt_storage[idcs[0]][idcs[1]] = arg;
502    };
503
504
505    template<>
506    inline void
507      Option< std::vector< char*>  >::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter& err )
508    {
509     
510      opt_storage[ idcs[ 0 ] ] = arg.empty() ? NULL : strdup(arg.c_str()) ;
511    };
512
513    template<>
514    inline void
515      Option< BoolAry1d >::parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter& err)
516    {     
517      xParseVec( arg, opt_storage );
518    };
519
520    template<>
521    inline void
522      Option< BoolAry2d >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter& err)
523    {     
524      xParseVec( arg, opt_storage[ idcs[0] ] );
525    };
526
527    template<>
528    inline void
529      Option< BoolAry3d >::parse(const std::string& arg, const IntAry1d& idcs, ErrorReporter& err )
530    {     
531      xParseVec( arg, opt_storage[idcs[0]][idcs[1]] );
532    };
533#else
534    template<>
535    inline void
536      Option< std::vector<bool> >::parse(const std::string& arg, ErrorReporter&)
537    {
538      char* pcNextStart = (char*) arg.data();
539      char* pcEnd = pcNextStart + arg.length();
540
541      char* pcOldStart = 0; 
542
543      size_t iIdx = 0; 
544
545      while (pcNextStart < pcEnd)
546      {
547        if ( iIdx < opt_storage.size() )
548        {
549          opt_storage[iIdx] = (strtol(pcNextStart, &pcNextStart,10) != 0);
550        }
551        else
552        {
553          opt_storage.push_back(strtol(pcNextStart, &pcNextStart,10) != 0) ;
554        }
555        iIdx++; 
556
557        if ( errno == ERANGE || (pcNextStart == pcOldStart) )
558        {
559          std::cerr << "Error Parsing Bools: `" << arg << "'" << std::endl;
560          exit(EXIT_FAILURE);
561        };   
562        while( (pcNextStart < pcEnd) && ( *pcNextStart == ' ' || *pcNextStart == '\t' || *pcNextStart == '\r' ) ) pcNextStart++; 
563        pcOldStart = pcNextStart;
564      }
565    }
566#endif
567#endif
568    /** Option class for argument handling using a user provided function */
569    struct OptionFunc : public OptionBase
570    {
571      typedef void (Func)(Options&, const std::string&, ErrorReporter&);
572
573      OptionFunc(const std::string& name, Options& parent_, Func *func_, const std::string& desc)
574      : OptionBase(name, desc), parent(parent_), func(func_)
575      {}
576
577#if NH_MV_SEI
578      void parse(const std::string& arg, const std::vector<int>& idcs, ErrorReporter& error_reporter)
579#else
580      void parse(const std::string& arg, ErrorReporter& error_reporter)
581#endif
582      {
583        func(parent, arg, error_reporter);
584      }
585
586      void setDefault()
587      {
588        return;
589      }
590
591    private:
592      Options& parent;
593      Func* func;
594    };
595
596    class OptionSpecific;
597    struct Options
598    {
599      ~Options();
600
601      OptionSpecific addOptions();
602
603      struct Names
604      {
605        Names() : opt(0) {};
606        ~Names()
607        {
608          if (opt)
609          {
610            delete opt;
611          }
612        }
613        std::list<std::string> opt_long;
614        std::list<std::string> opt_short;
615        OptionBase* opt;
616      };
617
618      void addOption(OptionBase *opt);
619
620      typedef std::list<Names*> NamesPtrList;
621      NamesPtrList opt_list;
622
623      typedef std::map<std::string, NamesPtrList> NamesMap;
624      NamesMap opt_long_map;
625      NamesMap opt_short_map;
626    };
627
628    /* Class with templated overloaded operator(), for use by Options::addOptions() */
629    class OptionSpecific
630    {
631    public:
632      OptionSpecific(Options& parent_) : parent(parent_) {}
633
634      /**
635       * Add option described by name to the parent Options list,
636       *   with storage for the option's value
637       *   with default_val as the default value
638       *   with desc as an optional help description
639       */
640      template<typename T>
641      OptionSpecific&
642      operator()(const std::string& name, T& storage, T default_val, const std::string& desc = "")
643      {
644        parent.addOption(new Option<T>(name, storage, default_val, desc));
645        return *this;
646      }
647
648#if NH_MV
649      template<typename T>
650      OptionSpecific&
651        operator()(const std::string& name, std::vector<T>& storage, T default_val, unsigned uiMaxNum, const std::string& desc = "" )
652      {
653#if NH_MV_SEI
654        std::vector<T> defVal;
655        defVal.resize( uiMaxNum, default_val ); 
656        std::vector< int > maxSize; 
657        maxSize.push_back( uiMaxNum ); 
658        parent.addOption(new Option< std::vector<T> >( name, storage, defVal, desc, false, maxSize ));
659
660        return *this;
661      }
662
663      template<typename T>
664      OptionSpecific&
665        operator()(const std::string& name, std::vector< std::vector<T> >& storage, T default_val, unsigned uiMaxNumDim1, unsigned uiMaxNumDim2, const std::string& desc = "" )
666      {
667        std::vector< std::vector<T> > defVal;
668        defVal.resize(uiMaxNumDim1);
669        for ( unsigned int idxDim1 = 0; idxDim1 < uiMaxNumDim1; idxDim1++ )
670        {
671          defVal[ idxDim1 ].resize(uiMaxNumDim2, default_val );         
672        }
673
674        std::vector< int > maxSize; 
675        maxSize.push_back( uiMaxNumDim1 ); 
676        maxSize.push_back( uiMaxNumDim2 ); 
677
678        parent.addOption(new Option< std::vector< std::vector<T> > >( name, storage, defVal, desc, false, maxSize ));
679        return *this;
680      }
681#else
682        std::string cNameBuffer;
683        std::string cDescBuffer;
684
685        storage.resize(uiMaxNum);
686        for ( unsigned int uiK = 0; uiK < uiMaxNum; uiK++ )
687        {
688          cNameBuffer       .resize( name.size() + 10 );
689          cDescBuffer.resize( desc.size() + 10 );
690
691          Bool duplicate = (uiK != 0); 
692          // isn't there are sprintf function for string??
693          sprintf((char*) cNameBuffer.c_str()       ,name.c_str(),uiK,uiK);
694
695          if ( !duplicate )
696          {         
697            sprintf((char*) cDescBuffer.c_str(),desc.c_str(),uiK,uiK);
698          }
699
700          cNameBuffer.resize( std::strlen(cNameBuffer.c_str()) ); 
701          cDescBuffer.resize( std::strlen(cDescBuffer.c_str()) ); 
702         
703
704          parent.addOption(new Option<T>( cNameBuffer, (storage[uiK]), default_val, cDescBuffer, duplicate ));
705        }
706
707        return *this;
708      }
709#endif
710#endif
711      /**
712       * Add option described by name to the parent Options list,
713       *   with desc as an optional help description
714       * instead of storing the value somewhere, a function of type
715       * OptionFunc::Func is called.  It is upto this function to correctly
716       * handle evaluating the option's value.
717       */
718      OptionSpecific&
719      operator()(const std::string& name, OptionFunc::Func *func, const std::string& desc = "")
720      {
721        parent.addOption(new OptionFunc(name, parent, func, desc));
722        return *this;
723      }
724    private:
725      Options& parent;
726    };
727
728  } /* namespace: program_options_lite */
729} /* namespace: df */
730
731//! \}
732
733#endif
Note: See TracBrowser for help on using the repository browser.