Go Down

Topic: #define vs. const (Read 774 times) previous topic - next topic

RayLivingston

#define somewhat something of the past for constants
#define is used for one helluva lot more than simply defining constants.
Just one, absolutely trivial, example:
Code: [Select]

#define getmax(a,b) ((a)>(b)?(a):(b))

And, of course, it can be abused to within an inch of its life.  This is ALL a single #define.  Anyone who does something like this should be taken out and shot, but it IS valid code, and WILL work.
Code: [Select]

#define DECLARE_MODIFICATION_REQUEST_PACKET( T )                                                \
namespace NameSpace                                                                     \
{                                                                                       \
                                                                                        \
class T##ElementModificationRequestPacket;                                                          \
}                                                                                       \
                                                                                        \
DECLARE_STREAMING_TEMPLATES( IMPEXP_COMMON_TEMPLATE_DECLARE, NameSpace::ElementModificationRequestPacket<T>, OtherNameSpace::NetPacketBase )    \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( NameSpace::ElementModificationRequestPacket<T> )     \
DECLARE_AUTOGENERATION_TEMPLATES( DLLIMPEXP_COMMON_TEMPLATE_DECLARE, NameSpace::T##ModificationRequestPacket, NameSpace::ElementModificationRequestPacket<T> )      \
                                                                                        \
namespace NameSpace {                                                                   \
class DLLIMPEXP_COMMON T##ModificationRequestPacket : public ElementModificationRequestPacket<T>\
{                                                                                       \
public:                                                                                 \
    T##ModificationRequestPacket( NetBase * pParent )                                   \
    : ElementModificationRequestPacket<T>( pParent ), m_Gen() {}                            \
                                                                                        \
    T##ModificationRequestPacket( NetBase * pParent,                                    \
                            Action          eAction,                                    \
                            const T &   rT )                                            \
    : ElementModificationRequestPacket<T>( pParent, eAction, rT ), m_Gen() {}               \
                                                                                        \
    T##ModificationRequestPacket( const T##ModificationRequestPacket & rhs )                        \
    : ElementModificationRequestPacket<T>( rhs ), m_Gen() {}                                \
                                                                                        \
    virtual                     ~T##ModificationRequestPacket( void ) {}                        \
                                                                                        \
    virtual Uint32          GetPacketTypeID( void ) const                           \
    {                                                                                   \
        return Net::T##_Modification_REQUEST_PACKET;                                        \
    }                                                                                   \
                                                                                        \
    virtual OtherNameSpace::ClassID GetClassID ( void ) const                           \
    {                                                                                   \
        return OtherNameSpace::NetBase::GenerateHeader( OtherNameSpace::ID__LICENSING,  \
                                                         Net::T##_Modification_REQUEST_PACKET );    \
    }                                                                                   \
                                                                                        \
    virtual T##ModificationRequestPacket * Create( void ) const                             \
    { return new T##ModificationRequestPacket( m_pParent ); }                                   \
                                                                                        \
    T##ModificationRequestPacket() {}                                                           \
                                                                                        \
protected:                                                                              \
    OtherNameSpace::ObjectAutogeneration<T##ModificationRequestPacket> m_Gen;                       \
                                                                                        \
    friend class OtherNameSpace::StreamingBase::StreamingClassInfoT<T##ModificationRequestPacket >;                     \
    OtherNameSpace::StreamingBase::Streaming<T##ModificationRequestPacket, ElementModificationRequestPacket<T> >    m_Stream;   \
                                                                                        \
};                                                                                      \
}                                                                                       \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( ThirdNameSpace::ListenerBase<const NameSpace::T##ModificationRequestPacket> )            \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( ThirdNameSpace::BroadcasterT<const NameSpace::T##ModificationRequestPacket> )            \
typedef  ThirdNameSpace::BroadcasterT<const T##ModificationRequestPacket>  T##ModifiedBroadcaster;

Regards,
Ray L.

J-M-L

Read about Preprocessor directives

for the other, I actualy did not mean to use "strong" in the programing language context (read this) just meant that when you do a define, the implicit type (int, signed int, long, char etc) is attached to the value whereas if your provide a type, the value will be forced to conform to that type....

but no big deal at this stage, focus on the basics ;)
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

J-M-L

#17
Mar 22, 2019, 08:12 pm Last Edit: Mar 22, 2019, 08:12 pm by J-M-L
#define is used for one helluva lot more than simply defining constants.
that was my point when I wrote "for constants" --> For constant or simple function, it's obsolete in my opinion (use const or inline) but it has other legit (or far fetched) use cases

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

Ken_F

Thanks Danois90 that was very informative.  I might even understand it.  I appreciate you taking the time.  If I understand you correctly, using #define IS NOT strong typing because it uses the default data type, but by defining a "const" variable as an 8-bit unsigned integer (const uint8_t) you are telling the compiler what to use and not letting the pre-processor make that default determination.  How close am I?

RayLivingston

#19
Mar 22, 2019, 11:42 pm Last Edit: Mar 23, 2019, 02:18 pm by RayLivingston
Thanks Danois90 that was very informative.  I might even understand it.  I appreciate you taking the time.  If I understand you correctly, using #define IS NOT strong typing because it uses the default data type, but by defining a "const" variable as an 8-bit unsigned integer (const uint8_t) you are telling the compiler what to use and not letting the pre-processor make that default determination.  How close am I?
The "type" of an expression defined using #define is a function of where/how the #define is actually used.  A #define HAS NO TYPE, because it is NOT a variable declaration.  It defines a literal string.
Code: [Select]

#define XYZZY 32
int8_t myInt8 = XYZZY;  //myInt8 is an 8-bit signed value, with value 32
int16_t myInt16 = XYZZY;  //myInt is a 16-bit signed value, with value 32

Code: [Select]

#define XYZZY (255)
uint8_t myuInt8 = XYZZY;  //myuInt8 is an 8-bit UN-signed value, with value 255
int8_t myInt8 = XYZZY;  //myInt8 is an 8-bit signed value, with value -1
int16_t myInt16 = XYZZY;  //myInt is a 16-bit signed value, with value 255


Regards,
Ray L.

MorganS

And it is the compiler which worries about types. The preprocessor is a text processor which doesn't even understand C.
"The problem is in the code you didn't post."

econjack

Defines can be "dangerous" if carelessly used, unless you are absolutely sure what you are doing, you should use const instead. Defines can be "overwritten" causing a define to change its value, const declarations will not allow this and therefore they are safer to use.

And for the memory usage: Same, same - either it fits or it doesn't! ;)
Anything can be dangerous if you don't understand what you're doing when you use it. I'm not sure what you mean by #define's being overwritten. Also, the fact that #define's are "typeless" can be an advantage. The macro:

#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))

will return the number of elements in any aggregate date type, which is an advantage, not a disadvantage.

Jiggy-Ninja

Is one better than the other? (faster, uses less memory. etc)
No.
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

jimLee

Then if you get tired of typing Serial.println(...) stuff you can do..

#define out Serial.print
#define outln Serial.println

I don't think you can do that with const.

You know.. I don't think I've ever actually used const.

-jim lee
PNW Ardiuno & Maker club
1012 9Th Street, Anacortes, WA 98221 (Around the back of building)

luni64

You can not do that "with const",  but of course you do not need a #define for it. The way you'd do that in c++ is


Code: [Select]
//#define out Serial.print

template< typename T > void out( T data )
{  
   Serial.print(data);
}
 
void setup()
{    
   out(5);  
   out(3.1415);
  //....
}

void loop()
{
}


Fast Stepper library? -> github.com/luni64/TeensyStep

bidouilleelec

........

Code: [Select]

#define XYZZY (255)
int8_t myuInt8 = XYZZY;  //myuInt8 is an 8-bit UN-signed value, with value 255



Regards,
Ray L.
"uint8_t myuInt8 = XYZZY;  //myuInt8 is an 8-bit UN-signed value, with value 255" ????

Regards,
bidouilleelec

J-M-L

#26
Mar 23, 2019, 09:34 am Last Edit: Mar 23, 2019, 10:28 am by J-M-L
Note also bad consequences of non typed constants when doing maths

Code: [Select]
#define oneMillis 1000 // in microsec
#define oneSec (1000*oneMillis)
#define oneMin (60*oneSec)
#define oneHour (60*oneMin)
#define oneDay (24*oneHour)
seems at first sight safe because we used parenthesis to protect calculated results

Challenge is that none of the litterals are typed and thus the compiler will take the smaller integral (int or larger) representation that fits and you'll overflow. Solution would be to add UL at the end of the litterals to force the compiler to know the type.

Contrast with typed constants
Code: [Select]
const uint32_t oneMillis = 1000; // in microsec
const uint32_t oneSec = (1000*oneMillis);
const uint32_t oneMin = (60*oneSec);
const uint32_t oneHour = (60*oneMin);
const uint32_t oneDay = (24*oneHour);
because in the formulas there is an long unsigned int the compiler knows about due to the type of the constant, all will be fine. 

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

gfvalvo

but of course you do not need a #define for it. The way you'd do that in c++ is
You don't need an ugly C++ template either:
Code: [Select]
Stream &out = Serial;

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  out.println("Hello World");
}

void loop() {
}
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

GolamMostafa

In this example the pre-processor will not modify anything. "PIN_NUMBER" will exist at compile time and it will occupy exactely one unsigned byte - because you coded it like that.
Will the symbolic name PIN_NUMBER be replaced by 00000011 (0x03) at run-time?

oqibidipo

You don't need an ugly C++ template either:
Code: [Select]
Stream &out = Serial;
···
  out.println("Hello World");

That's not the same, you can't do
Code: [Select]

out("blabbediblaa....");

Go Up