Consider following header file,
// filename : packet_types.h
#ifndef _PACKET_TYPES_
#define _PACKET_TYPES_
struct Packet {
enum Type {
typ_ONE,
typ_TWO,
typ_THREE,
};
static const char * STRINGS[] = {
"ONE",
"TWO",
"THREE"
};
const char * getText( int enum ) {
return STRINGS[enum];
}
};
#endif
As Arduino has limited memory, I don't want to include this portion of code when I include this file, packet_types.h,
static const char * STRINGS[] = {
"ONE",
"TWO",
"THREE"
};
const char * getText( int enum ) {
return STRINGS[enum];
}
But for my GUI application, I want the complete file. I want to use same file for both Arduino and GUI, but how can I add compiler directive #define,#ifdef, #ifndef... to do this job, when I include this file from some other file(main.cpp). Thanks.
Although it is possible to use some preprocessor juggling, the right solution is to simply use C++ as it was intended to be used. Only declare these class members and methods in the header file:
static const char * STRINGS[];
const char * getText( int enum );
And then define them in one of the source files:
const char * Packet::STRINGS[] = {
"ONE",
"TWO",
"THREE"
};
const char * Packet::getText( int enum ) {
return STRINGS[enum];
}
With the aforementioned preprocessor juggling, all it ends up accomplishing is the same logical result, but in a more confusing and roundabout way.
Related
Is it possible to declare static variable in a #define directive?
// header file
#define TEXT_ENUM
#ifdef TEXT_ENUM
#define TEXT_HANDLING_MACRO \
static const char * TEXT[]; \
static const char * getText( int _enum ) { \
return TEXT[_enum]; \
}
#else
#define TEXT_HANDLING_MACRO
#endif
struct Foo {
TEXT_HANDLING_MACRO
};
// cpp file
#include "foo.h"
const char * Foo::TEXT[] = {
"ONE",
"TWO",
"THREE",
0
};
How compiler will resolve static const char *, when I include this header file in some other file and try to access Foo::TEXT[].
Is it possible to declare static variable in a #define directive?
Yes, it is possible.
How compiler will resolve static const char *, when I include this header file in some other file and try to access Foo::TEXT[].
It's resolved at the linking stage.
You have to figure out what happens through the stages of compilations that the C compiler uses.
Since #define si a pre-compiler directive, everything will be resolved before actually compiling or looking at the code. Pieces of text (code, functions, whatever is included) will be passed on or filtered according to the directive.
All the rest happens after that, like compilation, that will look for global variable declaration, and linking, that will look for the address of those variable.
In you case, if you take your file and compile with a gcc compiler and the -E option (to do just the precompiler stage), you will get:
struct Foo {
static const char * TEXT[]; static const char * getText( int _enum ) { return TEXT[_enum]; }
};
const char * Foo::TEXT[] = {
"ONE",
"TWO",
"THREE",
0
};
I am having trouble creatiung a C++ library with a extern "C" function interface. I have the following header
#ifndef MYUTILITIES_H_
#define MYUTILITIES_H_
namespace MYUtilities {
static std::string UpperCase(std::string);
class MyException: public std::exception {
public:
MyException(std::string ss)
: s(ss) {
}
~MyException() throw () {
} // Updated
std::string s;
const char* what() const throw () {
return s.c_str();
}
};
} /* namespace ITGUtilities */
/*
* General utility functions
*/
extern "C" const char * UpperCase(const char *);
#endif /* MYUTILITIES_H_ */
source file
#include "ITGUtilities.h"
namespace ITGUtilities {
const char * UpperCase(const char * str) {
std::string tmp = str;
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper);
return tmp.c_str();
}
std::string UpperCase(std::string str) {
std::string tmp = str;
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper);
return tmp;
}
} // namespace ITGUtilities
In the .cpp source file I have tried defining the char *UpperCase(const char *) routine inside and outside of the namespace. In either case nothing that tries to use this library can find a reference for the extern function.
I have seen an example in the MySQL C++ cppcon/driver.h header where the extern definition is in the header but outside the namespace.
extern "C"
{
CPPCONN_PUBLIC_FUNC sql::Driver * get_driver_instance();
/* If dynamic loading is disabled in a driver then this function works just like get_driver_instance() */
CPPCONN_PUBLIC_FUNC sql::Driver * get_driver_instance_by_name(const char * const clientlib);
}
I just don't know what magic I should be using here.
help?
Use the curly braces to define your external C function:
extern "C"
{
UpperCase(const char *);
}
First of all, do not define extern functions in namespaces, it makes no sense. To check what was the name of generated function in object file, use utility called nm (on *nix). It should tell you of the name was mangled or not.
In your case, I assume the problem is that you have an overload with a different name and C++ linkage. I suggest you name your C-linkage function differently.
I know there are a couple other questions on this specific question, but nothing that I can find on it seems to work, so I'm posting my specific code.
Here is the code:
#ifndef __MEMORY_TRACKER_H__
#define __MEMORY_TRACKER_H__
#include <unordered_map>
namespace cige
{
namespace memory
{
class CIGE_API MemoryTracker
{
protected:
typedef struct AllocRecord
{
size_t bytes;
std::string filename;
size_t line;
std::string func;
AllocRecord() :
bytes(0), line(0)
{ }
AllocRecord(size_t sz, const char* file, size_t ln, const char* fun) :
bytes(sz), line(ln)
{
if (file)
filename = file;
if (fun)
func = fun;
}
} AllocRecord;
std::string m_leakFileName;
bool m_dumpToConsole;
typedef std::unordered_map<void*, AllocRecord> AllocMap;
AllocMap m_allocationMap;
size_t m_totalAllocations;
bool m_recordEnable;
protected:
void reportLeaks();
MemoryTracker() :
m_leakFileName("CIGEMemory.log"), m_dumpToConsole(true), m_totalAllocations(0), m_recordEnable(true)
{ }
public:
void setReportFileName(const std::string& name)
{
m_leakFileName = name;
}
const std::string& getReportFileName() const
{
return m_leakFileName;
}
void setReportToConsoleOutput(bool b)
{
m_dumpToConsole = b;
}
bool getReportToConsoleOutput() const
{
return m_dumpToConsole;
}
void setRecordEnable(bool b)
{
m_recordEnable = b;
}
bool getRecordEnable() const
{
return m_recordEnable;
}
size_t getTotalMemoryAllocated() const
{
return m_totalAllocations;
}
void _recordAlloc(void* ptr, size_t sz, const char* file = nullptr, size_t ln = 0, const char* fun = nullptr);
void _recordDealloc(void* ptr);
~MemoryTracker()
{
reportLeaks();
}
static MemoryTracker& get();
};
}
}
#endif // __MEMORY_TRACKER_H__
I'm getting: variable 'cige::memory::CIGE_API cige::memory::MemoryTracker' has initializer but incomplete type at the line with the class declaration. I've looked all over and I cant find any answers that have fixed this issue.
I'm also having the error expected '}' or ',' or ';' before 'protected' at the line with protected, right above the struct.
Any help with either of these two errors would be appreciated.
EDIT: CIGE_API is defined in a separate file (which is included), as __declspec(dllexport).
EDIT2: I fixed my problem (see the answer below). It was basically just Code::Blocks derping out pretty bad.
Looks like CIGE_API is not defined. So compiler try to resolve it like variable declaration class Type Variable {initializer-list}, where Type is CIGE_API and Variable is MemoryTracker.
In other words, syntactically you're predeclaring CIGE_API type and creating variable of this type instead of defining a class.
The definition
class CIGE_API MemoryTracker { ... };
is not valid C++. I guess CIGE_API is a macro defined to an implementation specific extension, but you didn't include the corresponding header which defines that macro.
Ok I ended up fixing my own problem. Code::Blocks wasn't properly finding files that were in my project (about the third time this has happened).
In entirely unrelated news, does anyone know another cross-platform IDE that works well for C++? (I already know about Eclipse).
I use SWIG to generate a C++ <--> Lua wrapper for a work project.
my main problem is, in this project at the base there exist type definitions for each platform. E.g. for Win32 there exists a header Win32Types.h where things like
typedef char Char;
typedef char TChar;
typedef signed int Int;
typedef unsigned int UInt;
typedef signed char Int8;
typedef unsigned char UInt8;
...
are defined.
The problem is now, with an example class like
class Named
{
public:
Named();
virtual ~Named();
void setName(const Char *name);
const Char* GetName() const;
}
, the setName- Method generated in the SWIG-wrapper looks something like this:
static int _wrap_Named_SetName(lua_State* L) {
int SWIG_arg = 0;
Named *arg1 = (Named *) 0 ;
Char *arg2 = (Char *) 0 ;
SWIG_check_num_args("Named::SetName",2,2)
if(!SWIG_isptrtype(L,1))
SWIG_fail_arg("Named::SetName",1,"Named *");
if(!SWIG_isptrtype(L,2))
SWIG_fail_arg("Named::SetName",2,"Char const *");
if (!SWIG_IsOK(SWIG_ConvertPtr(L,1,(void**)&arg1,SWIGTYPE_p_Named,0))){
SWIG_fail_ptr("Named_SetName",1,SWIGTYPE_p_Named);
}
if (!SWIG_IsOK(SWIG_ConvertPtr(L,2,(void**)&arg2,SWIGTYPE_p_Char,0))){
SWIG_fail_ptr("Named_SetName",2,SWIGTYPE_p_Char);
}
...
}
the problem here is, the wrapper tries to treat Char as just another class pointer, although it is just a char pointer renamed to Char.
is there any way to circumvent this behaviour?
i tried to write a typemap like
%typemap(in) Char {
$1 = lua_tostring($input);
}
, but im not sure i did it the right way...
There's two easier ways you can do this:
Show SWIG the typedefs for that platform, probably using %include
Tell SWIG to just use the normal unsigned char * typemap using %apply:
%apply unsigned char * { const Char * }
I have just found out that the following is not valid.
//Header File
class test
{
const static char array[] = { '1', '2', '3' };
};
Where is the best place to initialize this?
The best place would be in a source file
// Header file
class test
{
const static char array[];
};
// Source file
const char test::array[] = {'1','2','3'};
You can initialize integer types in the class declaration like you tried to do; all other types have to be initialized outside the class declaration, and only once.
You can always do the following:
class test {
static const char array(int index) {
static const char a[] = {'1','2','3'};
return a[index];
}
};
A couple nice things about this paradigm:
No need for a cpp file
You can do range checking if you want to
You avoid having to worry about the static initialization fiasco
//Header File
class test
{
const static char array[];
};
// .cpp
const char test::array[] = { '1', '2', '3' };
Now, in C++17, you can use inline variable
How do inline variables work?
A simple static data member(N4424):
struct WithStaticDataMember {
// This is a definition, no outofline definition is required.
static inline constexpr const char *kFoo = "foo bar";
};
In your example:
//Header File
class test
{
inline constexpr static char array[] = { '1', '2', '3' };
};
should just work
With constexpr you must define the value on the header even in C++11
If you use constexpr instead of const, then this answer suggests that you not only can, but must, define on header even in C++11:
#include <cassert>
struct MyClass {
static constexpr int is[] = {1, 2, 3};
static constexpr int i = 1;
};
// TODO is this ever mandatory? Create example that fails on -std=c++11.
// Pretty sure never mandatory in C++17 https://stackoverflow.com/a/40959093/895245
// constexpr int MyClass::is[];
int main (void) {
assert(MyClass::is[0] == 1);
assert(&MyClass::is[0] == &MyClass::is[1] - 1);
assert(MyClass::i == 1);
assert(&MyClass::i == &MyClass::i);
}
Compile and run with:
g++-10 -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
If instead you try:
struct MyClass {
static constexpr int is[];
};
constexpr int MyClass::is[] = {1, 2, 3};
compilation fails with:
main.cpp:4:26: error: ‘constexpr’ static data member ‘is’ must have an initializer
Tested on Ubuntu 20.04.
This is kind of an abuse of the system, but if you REALLY want to define it in the header file (and you don't have C++17), you can do this. It won't be a static member, but it will be a constant that only takes up storage per compilation unit (rather than per class instance):
(Put all of this code in the header file.)
namespace {
const char test_init_array[] = {'1', '2', '3'};
}
class test {
public:
const char * const array;
test() : array(test_init_array) {}
};