You are here:

C++/a wizard data type

Advertisement


Question
Hello...

I'm wondering if there a data type like that:

//
Data_Yype x;
int y=5,a;
cin>>a;
if (a==1) {
    put (cout<<) into x
}
else {
    put (cin>>) into x
}
xy;
//

then x will do the operation it contains:
prints the value of y
or reads a value to y

I need this kind of data type to do a lot of things in my assignment

//
void search (.....) {
  Data_Type x;
  if (i==0) {
      Clinics* currPtr;
      currPtr=Hospital[n].Clinic
      put (.name) into x
  }
  else if (i==1) {
      Doctors* currPtr;
      currPtr=Hospital[n].Clinic->Doctor
      put (.fname) into x
  }
  else if (i==2) {
      Doctors* currPtr;
      currPtr=Hospital[n].Clinic->Doctor
      put (.lname) into x
  }
  .
  .
  .
}
search for (of type string/int it depends) in currPtr(x)
// when i==2
//   currPtr(x) means Hospital[n].Clinic->Doctor.lname
Not Found, search on  currPtr->next

I know this is almost impossible.. I don't want to make a function for each case, but it seems it's the only way

Thanks...

Answer
In a word no. The types involved in the first case are different for a start, and the latter case is just bad C++ syntax.

However that does not mean that you cannot get similar effects. For example you could create a type constructed from two IO Stream object references (or pointers): one to a std::istream and the other to a std::ostream:

   in_or_out_type io( std::cin, std::cout );
   cin>>a;
   io(a, y);

The class in_or_out_type might look like so:

   class in_or_out_type
   {
   public:
       in_or_out_type( std::istream & in, std::ostream & out )
       : in_(in), out_(out)
       {
       }

       void operator()( int direction, int & value )
       {
         switch ( direction )
         {
         case 0:
         in_ >> value;
         break;

         case 1:
         out_ << value;
         break;

         default:
         throw std::invalid_argument
         ("direction should be 0 (in) or 1 (out)" );
         }
       }

   private:
       std::istream & in_;
       std::ostream & out_;
   };

Of course this only works for values that are of int type (as in your example). To make the call operator (operator()) more generic we can make it a member function template like so:

   class in_or_out_type
   {
   public:

   // ...

       template <typename T>
       void operator()( int direction, T & value )
       {
       // ...
       }
   }

   // ...

   };

Doing so will cause the compiler to generate one version of the call operator function for each value type you pass to it, so it does not obviously save code but does save typing. It might save code over the case of not using a member function template where you added lots of call operator overloads to cater for all predicated uses, some of which were not used.

Note that you will require a reasonably modern compiler to make use of member function templates.

Also note that the example code shown here is just that - an example. Like all code in this reply it is not production quality code, and I have not compiled it (so sorry for any typos or other errors!). They are also certainly not the only solutions, just the ones that occurred to me at the time of writing off the top of my head, so you might like to sit back and see if you can see any alternatives to the solutions presented here.

Another possibility would be to use polymorphism. The base would define the operation to be performed; the two derived variations would cater for the input and output cases. You would probably then have to wrap the pieces up in a class or function to pull it all together:


   class in_or_out_base { ... };
   class input  : public in_or_out_base { ... };
   class output : public in_or_out_base { ... };

   // ...

   in_or_out_base * create_new_in_or_out( int direction )
   {
       switch ( direction )
       {
       case 0:
         return new input( cin );

       case 1:
         return new output( cout );

       default:
         throw std::invalid_argument
         ( "direction should be 0 (in) or 1 (out)" );
       }
   }

The main sticking point I can see here is that a member function template cannot be virtual. This is because they would define a possibly infinite, but unknown at the time a class is defined, number of potential virtual functions so the compiler cannot know the size or contents of the structures necessary to manage virtual functions and polymorphic behaviour (typically this is in the form of a table, called the vtable that contains one pointer to each virtual function).

The way around this would be to make the three classes class templates and the function a function template each taking 1 parameter, the type of the value to be input or output.

My final point of warning with the current outline of this scheme is that it requires the dynamic creation of the input/output types so be sure they are deleted when no longer required (in fact using a smart pointer to hold them is probably a good idea).

Now to your second example. This case as far as I can tell from the example is that you wish to be able to search a single set of data by checking for matches to various different pieces of data held in the records.

You do not need to re-write the whole of the search logic for each search case. You only need to supply the correct comparison logic for each case. This is how you work with the algorithms in the C++ standard library, for example:

   #include <list>
   #include <algorithm>

   // ...

   class MatchClinicName
   {
       MatchClinicName( std::string const & name )
       : name_( name )
       {
       }

       bool operator( Hospital const & hospital ) const
       {
         return hospital.Clinic.name == name_;
       }
   
   private:
       std::string      name_;
   };


   class MatchDoctor1stName
   {
       MatchDoctor1stName( std::string const & name )
       : name_( name )
       {
       }

       bool operator( Hospital const & hospital ) const
       {
         return hospital.Clinic->Doctor.fname == name_;
       }
   
   private:
       std::string      name_;
   };

   // etc...

   // ...

   std:list<Hospital> hospitals;

   // ...

   std:list<Hospital>::iterator matched_position;

   matched_position =  std::find( hospitals.begin()
         , hospitals.end()
         , MatchClinicName("Antinatal")
         );

   // ...

   matched_position =  std::find( hospitals.begin()
         , hospitals.end()
         , MatchDoctor1stName("Edmond")
         );

Again, you will see that we are using classes that define a call operator, operator(). The name for such objects is Function Objects or Functors. In this case they represent what the C++ standard calls Unary Predicates. Unary because they take a single argument, and Predicate because they answer a true/false question, i.e. they return a Boolean value. The reason we need to do this is because we wish to perform specialised searches on values of specific members of the types in your collection and to do this we need two things:

- the value to compare against
- the logic to perform the comparison

The first is provided by the constructor and state of the functor classes and the latter by their operator() implementation.

I should note that the unary predicate operation need not be provided by a functor class instance. It could be implemented as an ordinary function if such will suffice. In this case however we need to associate the operation with some state - the value to match, so a functor class is the way to go.

The functors can be used with the standard library algorithms such as std::find. The algorithms work over one or more ranges, usually associated with a sequence of items in some collection. In this example the collection is a std::list of Hospital types (I have tried to use names for types from your example, but you did not show the type names only the object names - Hospital seems to be an array or other collection of some type representing hospital data in your example; I have used it as a type name in my example). The list begin and end member functions return iterators (generalisations of pointers, i.e. position) pointing to the first and one-past-the-last items in the list. The value returned from std::find is an iterator representing the position of the fist item in the sequence to match the criteria or the end value of the sequence searched if no match was found.

So each use of std::find tries to locate matching Hospital items in the sequence from the first to last Hospital objects in the hospitals list. It determines a match by passing each Hospital object (by const reference) to the operator() function of a copy of the temporary functor passed to find. The temporary functor instances are constructed using the value to be found.

In theory it would be possible to create a functor unary predicate that could compare the string value to one of a number of members of the Hospital type and its related members. This could be expanded to include int values as well:

   class HospitalMatch
   {
       HospitalMatch( int mode, std::string const & data )
       : strData_( data ), mode_( mode )
       {
       // check mode_ is valid, throw exception if not
       // e.g. std::invalid_argument
       }

       HospitalMatch( int mode, int data )
       : intData_( data ), mode_( mode )
       {
       // check mode_ is valid, throw exception if not
       // e.g. std::invalid_argument
       }

       bool operator( Hospital const & hospital ) const
       {
         switch ( mode_ )
         {
         case 0:
         return hospital.Clinic.name == strData_;
         case 1:
         return hospital.Clinic->Doctor.fname == strData_;
         case 2:
         return hospital.Clinic->Doctor.lname == strData_;
  
       // ...

         default:
         throw std::domain_error("Invalid HostitalMatch mode");
         };
       }
   
   private:
       std::string      strData_;
       int          intData_;
       int          mode_;
   };

You will notice that the above all-in-one matching functor class is a bit more complex that the classes used to match for individual cases and that it now has to protect against the match mode value being invalid, which means you will have to add code to handle these failures (i.e. try ... catch blocks).

If you wish to know more about C++ features such as templates then a good starting point is the C++ FAQ at: http://www.parashift.com/c++-faq-lite/. Section 35 is about templates. I suggest you read all of this before asking elsewhere - it is probable that many of your basic questions will be answered by the FAQ.

If you wish to know more about the facilities of the collections, algorithms etc. of the C++ standard library then I suggest you obtain a good reference such as "The C++ Standard Library A Tutorial and Reference" by Nicolai M. Josuttis. There is some on line reference material available at http://www.sgi.com/tech/stl/.

If you want more details abut templates then Nicolai M. Josuttis in conjunction with David Vandevoorde have written a good book on the subject: "C++ Templates The Complete Guide".  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.