falopsy
falopsy

Reputation: 636

Creating macro for operator <<

I know using macros is "generally" not considered good programming practise but I will like to know how macros are defined for operators. For example, several boost libraries have macros that can be used as (http://www.boost.org/doc/libs/1_55_0/libs/log/doc/html/log/detailed/sources.html):

BOOST_LOG(m_logger)<< "Message to log";

I have tried to look at the definition of the BOOST_LOG but that is also pointing to a different macro, which also point to another etc until I get to:

#define BOOST_LOG_STREAM_INTERNAL(logger, rec_var)\
    for (::boost::log::record rec_var = (logger).open_record(); !!rec_var;)\
        ::boost::log::aux::make_record_pump((logger), rec_var).stream()

My question is, if I have an operator << on a class, how can I turn it into a micro so I can do:

MY_MACRO << message.

Upvotes: 0

Views: 603

Answers (3)

Peter
Peter

Reputation: 36617

As you've noted, macros are usually best avoided in C++.

However, to indulge your curiosity (I do not recommend such things) .....

To do this, the macro would need to expand (eventually) to either (1) the name of (or maybe a reference to) an existing object that is in scope or (2) an expression that instantiates an object.

In both cases, the object would need to be of a type that has a suitable operator<<().

An example of the first is

 #include <iostream>

 #define A_MACRO std::cout

 int main()
 {
     A_MACRO << "Hello world\n";    // will write to std::cout
 }

A partial example of the second is

 class X
 {
      public:

         X() {  /*  set things up */ };

         X &operator<<(const char * s)
         {
               // do something with s

               return *this;
         };

         ~X()
         {
             // clean up whatever resources (file handles, etc) the constructor set up
         };
 };

 #define B_MACRO(SOMETHING)  SOMETHING()

 int main()
 {
       B_MACRO(X) << "Hello world\n";
 }

(This is partial, because X would, presumably, need to do something useful which I haven't implemented). This example requires the macro argument to be a type that has both a default argument and suitable operator<<(). The operator<<() may be either a member of a class (as I've done it above), a non-member friend function, or a non-member function that only accesses public members of X.

To understand better, read up on how the preprocessor works (e.g. how it goes about handling macros that expand to use of other macros, and how it handles macro arguments).

Practically, avoid doing this where possible in C++, The occasions you need to are rare (e.g. if you're writing a library that, like Boost, must work for a range of compilers with different features and bugs). Macros have a pile of undesirable traits, including ignoring scope. You would usually be better off using objects or types directly, than hiding their usage in a set of macros.

Upvotes: 1

Cpp plus 1
Cpp plus 1

Reputation: 1010

You can't use characters other than letters, numbers, and the dollar sign (which is not guaranteed to be supported by the standard) in creating macros. The libraries you are talking about might be wrapping whatever arguments it receives into a class with an overloaded >> operator.

For example:

class wrapper {
    int convertedType[];
    public:
        wrapper(aClassType a) {
            //Convert a to an array of integers
        }

        static wrapper createWrapper(aClassType a) {
            //Do the same thing as the constructor
        }

        aClassType toOriginalType(wrapper a) {
            //perform conversion to wrapper
        }

        operator<< {
            //perform operations on the converted type (int array in this example
        }
}

#define MACRO(x) wrapper::toOriginalType(wrapper::createWrapper(x) << 5)

If you create enough conversion methods, then it will be as if >> can be used in macros.

Upvotes: 0

Bathsheba
Bathsheba

Reputation: 234785

Yes you can do this.

MY_MACRO would evaluate to the instantiation of a class, say Foo.

So MY_MACRO << message becomes Foo() << message; after the preprocessor has done its job.

Foo then defines an overloaded << operator for types such as const char*, which accumulates the result. Furthermore, Foo has a destructor that actually outputs those accumulated results. This will be called once the anonymous temporary Foo has gone out of scope which will happen at the end of the statement.

Upvotes: 4

Related Questions