#defines a case for a valid use for them -- maybe

    General discussion

  • I think that it is generally understood that in most cases the use of #defines in C++ is to be discouraged, and for very good reasons. However, I have used a pattern that makes use of #defines for many years that I have not found a good alternative to. I am putting this onto the forum for discussion and do not mind being shot down in flames (on condition that a better alternative is given).

    The scenario is this. I am modelling electrical components and the pins on them. Within the model of a component I need to be able to access pins by enumeration (so that I can perform the component modelling logic). Pins on a component also need to have names that match the real life pin names on the component. I would like to make sure that the enumerations for pins always match up with the pin names and never get out of step. (If a new pin gets added I want to ensure that when adding it I do not accidentally misalign the enumeration and name.) The pattern I use for this is shown in the very much simplified example below. Imagine I am modelling a component that has real life pin names J1_A, J1_B, J1_C, J2_A, J2_B, J2_C and J2_D:

    // System includes...
    #include <iostream>
    #include <memory>
    #include <vector>
    #include <string>
    using namespace std;
    // The following #define is used to ensure that the pin names, and the
    // enumerations for the pins are kept in sync. To use it: -
    //        - Define PIN_ITEM suitably (e.g. to extract the pin name etc.)
    //        - Insert the text PINS_LIST as required.
    #define PINS_LIST                                                       \
    	/*         Name              Enumeration        */              \
    	PIN_ITEM ( "J1_A",           J1_A )                             \
    	PIN_ITEM ( "J1_B",           J1_B )                             \
    	PIN_ITEM ( "J1_C",           J1_C )                             \
    	PIN_ITEM ( "J2_A",           J2_A )                             \
    	PIN_ITEM ( "J2_B",           J2_B )                             \
    	PIN_ITEM ( "J2_C",           J2_C )                             \
    	PIN_ITEM ( "J2_D",           J2_D )                             \
    // End of PINS_LIST
    // Pin names.
    // Create an array of the names of all the pins. To do this we define
    // PIN_ITEM to extract the "Name" portion given in the PINS_LIST.
    #undef PIN_ITEM
    #define PIN_ITEM( Name, Enumeration ) Name,
    string PinNames[] = { PINS_LIST } ;
    // Pin enumerations.
    // Create an enueration for the pins. To do this we define PIN_ITEM to extract
    // the "Enumeration" portion given in the PINS_LIST. (We add NUM_PINS to the
    // enumeration so that we can tell how many pins there are.)
    #undef PIN_ITEM
    #define PIN_ITEM( Name, Enumeration ) Enumeration,
    enum Pin_e { PINS_LIST NUM_PINS } ;
    class Pin_c
    	public :
    		Pin_c( const char* Name )
    		: mName{Name}
    			// Nothing else to do in ctor
    		float GetVoltage() const
    			cout << "Dummy function to get voltage for pin " << mName << endl ;
    			return ( 0 ) ;
    	private :
    		string mName ;
    } ;
    int main( int argc, char *argv[] )
    	vector<unique_ptr<Pin_c>> Pins ;
    	for ( int ii = 0 ; ii < NUM_PINS ; ++ii )
    		unique_ptr<Pin_c> ThisPin{ new Pin_c{ PinNames[ii].c_str() } } ;
    		Pins.push_back( std::move(ThisPin) ) ;
    	// Access a couple of pins using their enumerations.
    	if ( Pins[ J1_A ]->GetVoltage() < Pins[J2_D]->GetVoltage() )
    		cout << "Pin J1_A voltage is less than J2_D voltage )" << endl ;
    		cout << "Pin J1_A voltage >= Pin J2_D voltage" << endl ;
    	return ( 0 ) ;

    The define PINS_LIST is the full list of the the pins in terms of PIN_ITEM. Before using PINS_LIST I make sure that PIN_ITEM is suitable defined. This is done when we create the array of pin names PinNames and the enumeration for the pins Pin_e.

    In the example the detail of the class Pin_c is not important, it is only there to show the kind of things I would like to do. The main function illustrates the kind of content a class that models a component would have. It would create pins, add them to a vector and access them using the Pin_e enumeration items.

    So the discussion point is this "Is this a valid use of #defines in C++ or is there a better way to do this kind of thing?"

    Any constructive comment is gladly welcomed!

    Thanks in advance,


    Monday, February 11, 2019 12:23 PM