Given the following snippet:
/* trie.h file */
using namespace std;
#include <list>
typedef struct tn {
char ch;
list<struct tn*> ptrs;
} TrieNode;
class Trie {
public:
static const TrieNode* EMPTY;
//... other member functions
};
/* trie.cpp file */
#include "trie.h"
// declare, define static variables of the Trie class
TrieNode* Trie::EMPTY = (TrieNode*) malloc( sizeof(TrieNode) ); // <-- seems to work fine
// the statements below seem to yield errors
Trie::EMPTY->ch = '.';
Trie::EMPTY->ptrs = nullptr;
I get the error: "This declaration has no storage type or type specifier" if I try to instantiate the struct member variables of the static constant variable EMPTY. I know storing EMPTYas a struct object rather than a pointer to the struct object would be easier but was curious how this would work. Thanks.
You can't put statements like Trie::EMPTY->ch = '.'; and Trie::EMPTY->ptrs = nullptr; in global scope, they can only be executed inside of functions, constructors, etc.
Try something more like this instead:
/* trie.h file */
#include <list>
struct TrieNode {
char ch;
std::list<TrieNode*> ptrs;
};
class Trie {
public:
static const TrieNode* EMPTY;
//... other member functions
};
/* trie.cpp file */
#include "trie.h"
// declare, define static variables of the Trie class
static const TrieNode emptyNode{'.'};
const TrieNode* Trie::EMPTY = &emptyNode;
Live Demo
In a namespace (outside any function) you may place only declarations.
Statements like these:
Trie::EMPTY->ch = '.';
Trie::EMPTY->ptrs = nullptr;
are not allowed to be placed in a namespace, because they are not declarations.
Moreover, this statement:
Trie::EMPTY->ptrs = nullptr;
does not make sense, because the object ptrs is not a pointer, and a std::list cannot be initialized from nullptr.
Pay attention to that instead of the C function malloc(), you should use the C++ operator new.
This definition:
TrieNode* Trie::EMPTY = (TrieNode*) malloc( sizeof(TrieNode) );
is also incorrect, because you forgot to specify the qualifier const.
It should be rewritten like this:
const TrieNode* Trie::EMPTY = new TrieNode { '.' };
Here is a demonstrative program
#include <iostream>
#include <list>
typedef struct tn {
char ch;
std::list<struct tn*> ptrs;
} TrieNode;
class Trie {
public:
static const TrieNode* EMPTY;
//... other member functions
};
// the definition below must be placed in a cpp module
// it presents here only for demonstration.
const TrieNode* Trie::EMPTY = new TrieNode { '.' };
int main()
{
return 0;
}
Before exiting the program you should free the allocated memory.
Instead of the raw pointer you could use the smart pointer std::unique_ptr.
Related
I'm fairly new to c++ so i'm having a bit of trouble wrapping my head around forward declarations.
I have two header files which #include eachother as they both have structs that reference eachother. I've seen from many other answers that I need to forward declare these structs, so that I can do this. Here is how i implemented that:
Parser.h
namespace Variables
{
struct Variable;
}
#include "variables.h"
namespace Parser
{
struct Node
{
int type;
Variables::Variable variable;
int skip;
Parser::Function fn;
Parser::Node *parent;
};
}
Variables.h
namespace Parser
{
struct Node;
}
#include "parser.h"
namespace Variables
{
typedef struct Variable
{
int type;
std::string name;
Variables::Expression int_value;
int scope;
Parser::Node *parent;
} Variable;
}
This seemed to work, however it complained about the Node struct having a member of incomplete type(the Variables::Variable) which I found out was because you have to do a const value when it's forward declared, so I made it a pointer:
struct Node
{
int type;
Variables::Variable *variable;
int skip;
Parser::Function fn;
Parser::Node *parent;
};
Is there a way that I can implement this where I don't have to make Variables::Variable a pointer?
For Example:
graph.h
#ifndef GRAPH_H
#define GRAPH_H
#include <iostream>
#include <string>
using namespace std;
class graph
{
private:
struct node
{
string name;
int currentValue;
struct node *next;
};
node* head;
public:
graph();
~graph();
graph(string* newName, int* givenValue);
}
#endif
graph.cpp
#include "graph.h"
graph::graph() {}
graph::~graph() {}
graph::graph(string* newName, int* givenValue)
{
//This is what I want to achieve
this->node->name = newName; //Compile error
}
main.cpp
#include "graph.h"
#include <iostream>
using namespace std;
int main()
{
return 0; //Note I have not yet created a graph in main
}
How can I access the struct node members for the function above?
This is the error:
graph.cpp: In constructor ‘graph::graph(std::string*, double*)’:
graph.cpp:24:8: error: invalid use of ‘struct graph::node’
this->node->label = newName;
The problem has nothing to do with your private struct. The constructor should be able to access all private members.
The problem as that you confused the struct name node and the variable name head:
this->node->name = newName; // incorrect
Instead you should write:
this->head->name = *newName;
If you want to access the class variable you should call
this->head->name = *newName;
though you can omit this-> so the following is fine
head->name = *newName;
Couple of other notes:
string* newName is a pointer so you need to access its value with the dereference operator "*" (i.e. head->name = *newName; instead of head->name = newName;
node* head is a pointer and currently you are trying to access an uninitialized pointer. You probably need something like head = new node(); as well.
Your problem is not related to private access. First, add ; to end your class declaration:
class graph
{
// ...
};
Then, you typed this->node->name while nodeis a type. Change this line for this->head->name. Note that the pointer head is uninitialized here.
And then, newName is of type string* while this->head->name is of type string. Depending on how you want to use your class, you may consider modifying your code like this:
graph::graph(const string& newName, int givenValue):
head(new node)
{
//This is what I want to achieve
this->head->name = newName;
}
Or like this:
graph::graph(string* newName, int* givenValue):
head(new node)
{
//This is what I want to achieve
this->head->name = *newName;
}
Also, read about rule of 3/5/0.
I'm trying to implement a class (C++) with an enum (with the permitted parameters). I got a working solution, but if I try to extend the functionality I get stuck.
Header data_location.hpp
class DataLocation
{
private:
public:
enum Params { model, period };
std::string getParamString(Params p);
};
Program data_location.cpp
string DataLocation::getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
The array ParamsStrings should be generally available in the class, because I need a second method (with inverse function) returning the enum value given a string.
If I try to define the array in the header I get the error:
in-class initialization of static data member ‘const char* DataLocation::ParamsStrings []’ of incomplete type
Why is the type incomplete? The compiler is for sure able to counts the strings in the array, isn't it?
In case there is no way to get my code working, is there an other way? With 1) no XML; 2) no double definition of the strings; 3) not outside the class; 4) no in code programmed mapping.
In class (header) use keyword static and initialize it outside (.cpp) without the static keyword:
class DataLocation {
public:
enum Params { model, period };
string getParamString(Params p);
static const char* ParamsStrings[];
// ^^^^^^
};
const char* DataLocation::ParamsStrings[] = {"MODEL", "BLLBLA"};
//^^^^^^^^^^^^^^^^^^^^^^^^
The code you have posted is perfectly fine.
Here's the proof:
#include <iostream>
#include <string>
struct DataLocation
{
enum Params { model, period };
std::string getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
};
int main()
{
auto a = DataLocation();
std::cout << a.getParamString(DataLocation::model) << std::endl;
return 0;
}
The error message you are getting is not to do with definition of a static data member in an inline function - that's allowed.
There's something else you're not showing us.
The main issue in my question (the second part) was that if I split the class in .hpp and .cpp the definition of the array (I mixed *char and string) has also to be split:
// data_location.hpp
class DataLocation {
static const char * ParamsStrings[];
}
// data_location.cpp
const char * ParamsStrings[] = {"MODEL", "PERIOD"};
At the end I introduced a consistency check to be sure that the number of values in enum growths as the number of strings. Because the array in C++ is somehow limited I had to go for a std::vector (to get the size).
Code for data_location.hpp
#ifndef DATA_LOCATION_HPP_
#define DATA_LOCATION_HPP_
#include <string>
#include "utils/dictionary.hpp"
extern const char* ENV_DATA_ROOT;
struct EDataLocationInconsistency : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct EDataLocationNotValidParam : std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DataLocation
{
private:
std::string mRootLocation;
static const std::vector<std::string> msParamsStrings;
static bool msConsistenceCheckDone;
public:
DataLocation();
std::string getRootLocation();
std::string getLocation(Dictionary params);
enum Params { model, period, LAST_PARAM};
std::string Param2String(Params p);
Params String2Param(std::string p);
};
#endif
Code for data_location.cpp
#include "data_location.hpp"
#include <string>
#include <cstdlib>
using namespace std;
const char* ENV_DATA_ROOT = "DATA_ROOT";
bool DataLocation::msConsistenceCheckDone = false;
DataLocation::DataLocation() {
mRootLocation = std::getenv(ENV_DATA_ROOT);
if (not msConsistenceCheckDone) {
msConsistenceCheckDone = true;
if (LAST_PARAM+1 != msParamsStrings.size()) {
throw(EDataLocationInconsistency("DataLocation: Check Params and msParamsStrings"));
}
}
}
string DataLocation::getRootLocation() {
return mRootLocation;
}
string DataLocation::getLocation(Dictionary params) {
// to do
return "";
}
const vector<string> DataLocation::msParamsStrings = { "MODEL", "PERIOD", ""};
string DataLocation::Param2String(Params p) {
if (p>=msParamsStrings.size()) {
throw(EDataLocationNotValidParam("Parameter not found"));
}
return msParamsStrings[p];
}
DataLocation::Params DataLocation::String2Param(string p) {
for (int i = 0; i < msParamsStrings.size(); i++) {
if (p == msParamsStrings[i])
return (Params)i;
}
throw(EDataLocationNotValidParam("Parameter not found"));
}
And also a unit test:
#include <boost/test/unit_test.hpp>
#include "data_location.hpp"
#include <string>
using namespace std;
BOOST_AUTO_TEST_SUITE( data_location )
BOOST_AUTO_TEST_CASE(data_location_1) {
DataLocation dl;
auto s = dl.getRootLocation();
BOOST_CHECK_EQUAL(s, "/home/tc/data/forex" );
BOOST_CHECK_EQUAL(dl.Param2String(DataLocation::period),"PERIOD");
BOOST_CHECK_EQUAL(dl.String2Param("PERIOD"),DataLocation::period);
BOOST_CHECK_THROW(dl.String2Param("SOMETHING"), EDataLocationNotValidParam);
BOOST_CHECK_THROW(dl.Param2String((DataLocation::Params)100), EDataLocationNotValidParam);
}
BOOST_AUTO_TEST_SUITE_END()
C++ is very picky about what it will let you initialize inside of a class definition; there are some particularly non-intuitive rules surrounding static members. It all has to do with the ODR, and why all the rules are the way they are is not especially important.
To cut to the chase, making your array a static constexpr const member should shut the compiler up. With the C++11 standard, the restrictions were relaxed a bit, and one of the new stipulations was that static constexpr members can be initialized inline. This is perfect for your application, since the strings in your array are compile-time constants.
The recent g++ compiler which support C++0x or later compiles thus code. Pure C compile compiles, too. Because strings in initialization like {"MODEL", "PERIOD"}; implemented as const char * pointer to the char array.
I don't understand pointers and references very well yet, but I have a class with static methods and variables that will be referenced from main and other classes. I have a variable defined in main() that I want to pass to a variable in this class with static functions. I want those functions to change the value of the variable that is seen in the main() scope.
This is an example of what I am trying to do, but I get compiler errors...
class foo
{
public:
static int *myPtr;
bool somfunction() {
*myPtr = 1;
return true;
}
};
int main()
{
int flag = 0;
foo::myPtr = &flag;
return 0;
}
Provide the definition of the static variable outside the class as:
//foo.h
class foo
{
public:
static int *myPtr; //its just a declaration, not a definition!
bool somfunction() {
*myPtr = 1;
//where is return statement?
}
}; //<------------- you also forgot the semicolon
/////////////////////////////////////////////////////////////////
//foo.cpp
#include "foo.h" //must include this!
int *foo::myPtr; //its a definition
Beside that, you also forgot the semicolon as indicated in the comment above, and somefunction needs to return a bool value.
#include <iostream>
using namespace std;
class foo
{
public:
static int *myPtr;
bool somfunction() {
*myPtr = 1;
return true;
}
};
//////////////////////////////////////////////////
int* foo::myPtr=new int(5); //You forgot to initialize a static data member
//////////////////////////////////////////////////
int main()
{
int flag = 0;
foo::myPtr = &flag;
return 0;
}
I cannot figure the syntax to declare a function pointer as a static member.
#include <iostream>
using namespace std;
class A
{
static void (*cb)(int a, char c);
};
void A::*cb = NULL;
int main()
{
}
g++ outputs the error "cannot declare pointer to `void' member". I assume I need to do something with parentheses but void A::(*cb) = NULL does not work either.
I introduced a typedef, which made it somewhat clearer in my opinion:
class A
{
typedef void (*FPTR)(int a, char c);
static FPTR cb;
};
A::FPTR A::cb = NULL;
void (*A::cb)(int a, char c) = NULL;