Getting compile errors with Copliens 1994 counted pointer example code - c++

OK, I am reading Copliens C++ Idioms book, and trying to run the handle/body examples in the book. After typing in the code, I am getting compile errors:
Here is the code the String and StringRep classes.
#ifndef _STRINGREP_H_
#define _STRINGREP_H_
#include <stdio.h>
#include <string.h>
class String;
class StringRep {
friend class String;
public:
StringRep() {*(rep = new char[1])='\0';}
StringRep(const StringRep& s) {
::strcpy(rep=new char[::strlen(s.rep)+1], s.rep);
}
~StringRep() { delete [] rep;}
StringRep(const char* s) {
::strcpy(rep=new char[::strlen(s)+1], s);
}
String operator+(const String& s) const {
char *buf = new char[s->length() + length() + 1];
::strcpy(buf, rep);
::strcat (buf, s->rep);
String retval(&buf);
return retval;
}
int length() const { return ::strlen(rep); }
void print() const {::printf("%s\n", rep); }
private:
StringRep(char ** const r) {
rep = *r;
*r = 0;
count = 1;
};
char *rep;
int count;
};
#endif
#ifndef _STRING_H_
#define _STRING_H_
#include <stdio.h>
#include <string.h>
#include "StringRep.h"
class String {
friend class StringRep;
public:
String operator+(const String& s) const {return *p + s;}
StringRep* operator->() const {return p;}
String() {
(p = new StringRep())->count = 1;
}
String (const String &s) { (p=s.p)->count++;}
String(const char* s) {
(p = new StringRep(s))->count = 1;
}
String operator=(const String& s) {
if (--p->count <=0) delete p;
(p = s.p)->count++;
return *this;
}
~String() { if (--p->count <= 0) delete p;; }
private:
String(char **r) {
p = new StringRep(r);
}
StringRep *p;
};
#endif
And a main.cc
#include <stdio.h>
#include <string.h>
#include "StringRep.h"
#include "String.h"
int main() {
String a("abcd"), b("efgh");
printf("a is "); a->print();
printf("b is "); b->print();
printf("concat of a+b is "); (a+b)->print();
return 0;
}
Compile errors;
GNU C++ version 4.1.2 20080704 (Red Hat 4.1.2-44) (x86_64-redhat-linux)
compiled by GNU C version 4.1.2 20080704 (Red Hat 4.1.2-44).
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 2d02d8750f9b337bb19a7dd5b4e2167e
StringRep.h: In member function 'String StringRep::operator+(const String&) const':
StringRep.h:21: error: return type 'struct String' is incomplete
StringRep.h:22: error: base operand of '->' has non-pointer type 'const String'
StringRep.h:24: error: base operand of '->' has non-pointer type 'const String'
StringRep.h:25: error: variable 'String retval' has initializer but incomplete type
String.h: In member function 'String String::operator+(const String&) const':
String.h:13: error: conversion from 'void' to non-scalar type 'String' requested
I figure that I cannot use String class until it is completely defines. Changing the
signature of the function to
String& operator+(const String& s) const {...
solves the first error, but causes the same error to show up where I am creating a
new String object
String retval(&buf);
I realize that the book I have is the 1994 reprint that I picked up. So can someone either point me newer code (if C++ coding style has changed) or point out how to fix this?
Thanks

You got a circular reference since StringRep needs to know the full definition of String to construct it in operator+. I advice to not put everything in header files, but just the declarations of the member functions and put the definitions in the .cpp (or .cc) file. That should fix it. That is also how code should be split if the class itself and / or the functions are not a template.

You need to pull the definition and declaration of everything apart. If you really want to put them in the header file, you can do so as follows:
<forward declarations, required includes>
<class itself, no functions defined>
<includes for forward declarations that are needed for function bodies>
<function bodies>
That will always work.

You can't include the String header file in the StringRep header file when you also include the StringRep header file in the String header file. This causes a circular inclusion and will prevent your code from compiling.
Think of it as:
StringRep.h loads String.h
which loads StringRep.h
which loads String.h
which loads StringRep.h
which loads ...
You can avoid this by moving the function declarations into the .cpp files and simply forward declaring the String class inside of StringRep.h (which you already do!). It won't actually try and resolve the reference to the String class until it actually compiles the code, by which time the circular inclusion issue will have been avoided.

Related

How can I solve a problem that undefined reference to constructor and destructor function of my own class?

I wrote a simple program that uses a class with a constructor, a destructor and a data member. When I tried to initialize an object, the debugger told me that there was not a destuctor and a destructor.I tried it in Clion, VS2019 and VSCode. And I got the same result like 4). I don't know it occurs all the time when I have actually created the constructor and destructor.
Pls help me. THANKS!
1) String.h
#ifndef TEST_STRING_H
#define TEST_STRING_H
class String {
public:
explicit String(const char *cstr = nullptr);
String(const String &str);
String &operator=(const String &str);
~String();
char *get_c_str() const { return m_data; }
private:
char *m_data;
};
#endif // TEST_STRING_H
2) String.cpp
#include "String.h"
#include <cstring>
inline String::String(const char *cstr) {
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
} else {
m_data = new char[1];
*m_data = '\0';
}
}
inline String::~String() { delete[] m_data; }
inline String &String::operator=(const String &str) {
if (this == &str)
return *this;
delete[] m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
inline String::String(const String &str) {
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
3) main.cpp
#include <iostream>
#include "String.h"
using namespace std;
int main() {
String s1("hello");
return 0;
}
4) The result
/tmp/ccCIK0hs.o: In function `main':
/home/Projects/test/main.cpp:7: undefined reference to `String::String(char const*)'
/home/Projects/test/main.cpp:7: undefined reference to `String::~String()'
collect2: error: ld returned 1 exit status
There are "problems" with the way you used inline specifier. The fix is simply to remove the inline specifier from all String method definitions to make the error go away.
Let's delve into why your program is ill-formed.
I'll be using the points from cppreference inline specifier:
An inline function or inline variable (since C++17) has the following properties:
The definition of an inline function or variable (since C++17) must be reachable in the translation unit where it is accessed (not necessarily before the point of access).
An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties:
There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit and (for non-static inline functions and variables (since C++17)) all definitions are identical. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.
It must be declared inline in every translation unit.
It has the same address in every translation unit.
Ad 1) In simple words: If you have an inline function definition like inline String::String(const char *cstr) { something } you are allowed to call it from the same .cpp file. If you call it from other .cpp file, like you defined function in String.cpp but called it from main.cpp, that's invalid.
Ad 2) Your functions are not static, so this section applies.
Ad 2.1) If you have inline String::String(const char *cstr) in your String.cpp, you have to have inline String::String(const char *cstr); in class declaration in String.h. If you declare it with inline, it has to be inline everywhere, in String.h too. Right now in your code, in String.h it does not have inline.
The fix is to adhere to rule in points 1 and 2.1. So either remove the inline keyword from String.cpp and have all your functions just be normal functions with external linkage, which is the normal way of programming.
Alternatively, if you have to have your functions with inline move all code from String.cpp to String.h and add inline to those member function declarations. Like the following code:
#ifndef TEST_STRING_H
#define TEST_STRING_H
class String {
public:
inline explicit String(const char *cstr = nullptr);
inline String(const String &str);
inline String &operator=(const String &str);
inline ~String();
inline char *get_c_str() const { return m_data; }
private:
char *m_data;
};
#include <cstring>
inline String::String(const char *cstr) {
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
} else {
m_data = new char[1];
*m_data = '\0';
}
}
inline String::~String() { delete[] m_data; }
inline String &String::operator=(const String &str) {
if (this == &str)
return *this;
delete[] m_data;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
inline String::String(const String &str) {
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
#endif // TEST_STRING_H
Note that functions defined" inside" class definition are implicitly inline, so you could also move your function definition inside class definition.

C++ polymorphic Print function not resolving properly

I have created a class that abstracts a SPI flash chip library called SerialFlash by creating an abstract class of Print.h. When I try to print to this by using the ArduinoJson library, I get an error:
src/FlashMemory.cpp:99:36: error: no matching function for call to 'ArduinoJson::JsonObject::printTo(<unresolved overloaded function type>)'
root.printTo(serialFlashPrint);
^
lib/ArduinoJson/include/ArduinoJson/Internals/../Internals/JsonPrintable.hpp:34:10: note: size_t ArduinoJson::Internals::JsonPrintable<T>::printTo(Print&) const [with T = Ardu
inoJson::JsonObject; size_t = unsigned int]
size_t printTo(Print &print) const {
^
lib/ArduinoJson/include/ArduinoJson/Internals/../Internals/JsonPrintable.hpp:34:10: note: no known conversion for argument 1 from '<unresolved overloaded function type>' to
'Print&'
The file referenced in the error above is here: https://github.com/bblanchon/ArduinoJson/blob/master/include/ArduinoJson/Internals/JsonPrintable.hpp
This is the header file for the class:
#include <Arduino.h>
#include <SerialFlash.h>
#include "Print.h"
#ifndef _SerialFlashPrint_h_
#define _SerialFlashPrint_h_
class SerialFlashPrint : public Print {
public:
SerialFlashPrint(SerialFlashFile *file);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buffer, size_t size);
private:
char buf[1];
uint16_t _current_byte;
SerialFlashFile * _file;
};
#endif
And the cpp file:
#include "serialFlashPrint.h"
SerialFlashPrint::SerialFlashPrint(SerialFlashFile * file) : Print() {
this->_file = file;
this->_current_byte = 0;
}
size_t SerialFlashPrint::write(uint8_t c) {
if(_current_byte == 0){
_file->erase();
_file->seek(0);
}
sprintf(buf, "%c", c);
_file->write(buf, 1);
_current_byte++;
return 0;
}
size_t SerialFlashPrint::write(const uint8_t *buffer, size_t size){
_file->erase();
_file->seek(0);
_file->write(buffer, size);
_file->write(NULL, 1);
return 0;
};
Generally, you use print function as: the root.printTo(Serial). This code is based upon an abstraction (which I got to work previously) called Chunked output that can be seen here: https://github.com/bblanchon/ArduinoJson/wiki/Bag-of-Tricks
Does anyone have any clues for me to figure out why I am getting <unresolved overloaded function type> instead of Print&?
<unresolved overloaded function type> means that the compiler found a function with several overloads and doesn't know which one to use.
You most likely have several serialFlashPrint() in your code or libraries.
If not, then you may have triggered the Most vexing parse:
SerialFlashPrint serialFlashPrint; // <- creates an instance of SerialFlashPrint
SerialFlashPrint serialFlashPrint(); // <- declares a function returning a SerialFlashPrint

Error: variable '*' has initializer but incomplete type and one other

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).

c++ Set not compiling properly

I am creating a data structure but when I try and compile I get an error saying that I haven't specified that type of set that I am initializing.
I am working with the NTL library with is used for large numbers.
This is my code:
#include <set>
#include ...
NTL_CLIENT
using namespace std;
using namespace NTL;
const RR ZERO = to_RR(0);
const RR ONE = to_RR(1);
const RR TWO = to_RR(2);
class tenTree
{
public:
tenTree(string newName = "", int newLevel = 0);
~tenTree();
void put(string prefix, RR power);
bool get(string prefix, RR & output);
void display(int depth);
bool isKnown(RR power){return (powers.find(power) != powers.end());};
private:
tenTree* children [10];
set<int> powers;
int level;
string name;
bool child[10];
};
When I try to compile it comes back with an error saying:
twoPow.cpp:47: error: ISO C++ forbids declaration of \u2018set\u2019 with no type
twoPow.cpp:47: error: expected \u2018;\u2019 before \u2018<\u2019 token
twoPow.cpp: In member function \u2018bool tenTree::isKnown(NTL::RR)\u2019:
twoPow.cpp:44: error: \u2018powers\u2019 was not declared in this scope
Is there something that I am missing here?
It was just a matter of the scope. All I had to do was add an std:: before the set and it compiled correctly.

How to build a binary from multiple c++ files?

I have started learning C++, and have gotten stuck when working with multiple files. To practice basic classes, I wrote three different files,
working.cpp
word.cpp
word.h
word.cpp:
#include <iostream>
#include "word.h"
using namespace std;
class word{
public:
char *word;
void createWord(char *str)
{
word = str;
}
void print_word(void)
{
cout<<word<<endl;
}
char * getWord()
{
return word;
}
}
working.cpp
#include <iostream>
#include "word.h"
void printWord(word);
using namespace std;
int main()
{
word one;
one.createWord("one");
printWord(one);
}
void printWord(word a)
{
cout<<a.getWord()<<endl;
}
word.h
class word;
These are three different files, so I am not sure how to compile them. What I have tried is
g++ working.cpp word.cpp
However, the compiler doesn't recognize word as a class, and gives me the following errors
working.cpp: In function 'int main()':
working.cpp:7:7: error: aggregate 'word one' has incomplete type and cannot be defined
working.cpp:7:12: error: aggregate 'word two' has incomplete type and cannot be defined
working.cpp:7:17: error: aggregate 'word three' has incomplete type and cannot be defined
working.cpp: In function 'void printWord(word)':
working.cpp:19:6: error: 'aha' has incomplete type
In file included from working.cpp:2:0:
word.h:2:7: error: forward declaration of 'class word'
word.cpp:25:1: error: expected ';' after class definition
What am I doing wrong while compiling?
You need to include more of the definition of word in the header file. Something like this:
class word
{
public:
char *word;
void createWord(char *str);
void print_word(void);
char * getWord();
};
Then, change word.cpp to just have the implementations:
void word::createWord(char *str)
{
word = str;
}
void word::print_word(void)
{
cout<<word<<endl;
}
char * word::getWord()
{
return word;
}
compile and link!
You need to have more of the word class in the header so that your other translation unit can know how big the class is (to reserve enough space for the instance you're creating) as well as to know the names of the methods you want to call.
Just mentioning the class name in the header file (a so-called forward declaration) is not enough; you need a complete class declaration (which declares all the fields and functions of the class):
class word {
public:
char *word;
void createWord(char *str);
void print_word(void);
char * getWord();
};
There is no actual declaration of class word in word.h
word.h:2:7: error: forward declaration of 'class word'
I would advise you to read Bjarne Stroustrup's brilliant book "The C++ Programming Language" to get started.