const char*, extern const char*, My Custom Library

Hi,

I have made a custom library which includes all of my required functions. Basically, It is related to WiFi, and MQTT which required certain parameters such as SSID, Password, Server address, Client ID etc. For example, I define Client ID in sketch as

const char* CLIENT_ID = "my_id";

and passed it to my library in .cpp file as following;

extern const char* CLIENT_ID;

Everything is working as required. But now I want to implement one more thing, If CLIENT_ID is defined in sketch, it should be used as it is, and if it is not defined in sketch, then CHIP_ID should become the CLIENT_ID which is required for connecting to MQTT server as client. I also have managed to set client id in my library as CHIP_ID. But both scenario does not work together. If I use

#ifndef CLIENT_ID 
#define CLIENT_ID "my_other_id"
#endif

in my header file and define it in sketch as

#define CLIENT_ID "my_id"

then defining CLIENT_ID in sketch does not pass to my library.

May please someone help in this particular issue?

this declares a variable:

const char* CLIENT_ID = "my_id";

this is a preprocessor directive:

#define CLIENT_ID "my_other_id"

apples and oranges, kind-of-thing happening there.

and passed it to my library in .cpp file as following;

The extern keyword does NOT pass a value. It makes a variable declared in another file available in the file containing the extern keyword.

Preprocessor directives are evaluated at compile time, NOT at run time.

It really is not clear what you are trying to do. Explain that in English, not code.

I think, I did not explain my problem well. Here are contents of sketch, header and .cpp file.

Sketch:

#define CLIENT_ID "MY_ID"

Header:

#define IDS 0

#ifndef CLIENT_ID 
#define IDS 1
#endif

.cpp

if(IDS == 1)
	{
		Serial.println("client id is not define");
		_CLIENT_ID = String(ESP.getChipId());
		Serial.println(CLIENT_ID);
		Serial.println(_CLIENT_ID);
	}
	else
	{
		_CLIENT_ID = CLIENT_ID;
		Serial.println("client id is already define");
		Serial.println(CLIENT_ID);
		Serial.println(_CLIENT_ID);
	}

_CLIENT_ID is a String variable which is defined in header class private: section.

Error:
error: 'CLIENT_ID' was not declared in this scope _CLIENT_ID = CLIENT_ID;

A #define statement's scope is limited to the compilation unit it is defined in.

A sketch is one compilation unit. A library is one or more compilation units.

You can not define a name in one compilation unit and expect it to influence another compilation unit, unless you define it in a header file included in both compilation units.

Why don’t you just create a member function to pass the string into the class?

void MyClass::setClientId(const char* clientId = nullptr)
{_clientId = clientId;}


void MyClass::someFunction()
{
  if (_clientId)
  {
  ...

It wouldn’t make the call to setClientId() compulsory.

BulldogLowell, Well mentioned. It does not come to my mind. I'll consider it now. But I have one more question in my mind.

All the things such as call to connect to MQTT server will get initiated with instance of library. And I'll have to call that setClientId(const char* clientId = nullptr) function in void setup() function of my sketch. It will be too late then, however it will work. But in the mean time, I'll get two connection notification for single unit on my MQTT server with two different client ids.

For more information, have a look on to my library at github.

PaulS:
The extern keyword does NOT pass a value. It makes a variable declared in another file available in the file containing the extern keyword.

It's exactly this kind of confusion that can be made more clear if there's a distinction made between define and declare. The extern keyword allows the compiler to construct a declaration, or an attribute list (e.g., its type, its scope, its ID), for a variable that is defined in another file. The definition of that variable is in some other file and means that its memory address is unknown to the compiler in the current file. The extern keyword is simply saying: "This variable is defined and has memory allocated for it in some other file, but let me use it in this file as..." followed by that variable's attribute list. It's the linker's responsibility to resolved the extern memory address. Simply stated, declarations are attribute lists (i.e., the properties of the variable) while a definition has both the attribute list, but also defines storage for the variable. The extern keyword simply allows the attribute list to be known in a file other than the file where the variable is defined.

vickeyhort:
All the things such as call to connect to MQTT server will get initiated with instance of library.

in that case, how about passing the string as one of parameters of the constructor?

** side note: be sure to be mindful not to attempt to futz around with the hardware during the creation of global objects... things like setting pinMode and such.

here are some pseudo code ramblings...

#include "MyClass.h"

const char* CLIENT_ID = "someSuperSecretClientIdentification";

MyClass someInstance = MyClass(CLIENT_ID);

and then your constructor would look similar the above:

header:

#ifndef MYCLASS_H
#define MYCLASS_H

#include <Arduino.h>
#include "MQTT.h"  //example

class MyClass{
  public:
    MyClass(const char* id = nullptr);
    // ... and so on...

  private:
    const char* clientId;  // this is a pointer to an immutable char[] object...
   // ... and so on...
};


#endif

implementation:

#include "MyClass.h"
#include <Arduino.h>
#include "MQTT.h"

MyClass::MyClass(const char* id)
{
  clientId = id;
  // do some MQTT stuff, for example
  
}

BulldogLowell:
in that case, how about passing the string as one of parameters of the constructor?

Or, maybe pass everything to a ".begin()" method called from setup() and do everything there instead of at instantiation?

gfvalvo:
Or, maybe pass everything to a ".begin()" method called from setup() and do everything there instead of at instantiation?

:slight_smile:

that was the first option! (reply 5)

BulldogLowell:
:slight_smile:

that was the first option! (reply 5)

Dooh!!!