The following sample does not compile, complaining that
In file included from /usr/include/msgpack.hpp:18:
/usr/include/msgpack/object.hpp:211:3: error: member reference base type 'logd::log_level' is not a structure or union
and a corresponding error for the other enum class. My question is how, using msgpack's c++ api, does one serialize a class with members that are of c++11 enum class type?
#ifndef LOG_MSG_HPP_
#define LOG_MSG_HPP_
#include <stdlib.h>
#include <msgpack.hpp>
/** #namespace logd */
namespace logd {
enum class log_level { SILENT,... DEBUG };
enum class log_domain { AI, ... MISC };
class log_msg {
public:
log_msg(log_level lev, log_domain dom, std::string msg);
log_level level();
log_domain domain();
std::string message();
~log_msg();
MSGPACK_DEFINE(lev_, dom_, msg_);
private:
log_msg();
log_level lev_ {log_level::DEBUG};
log_domain dom_ {log_domain::MISC};
std::string msg_ {"No message given."};
};
} /* namespace logd */
#endif /* LOG_MSG_HPP_ */
NOTE: Since the enums are mine, I can happily modify them to make msgpack happy. Unfortunately, I can find no references on the subject in their docs or the first couple pages of Google. I am also not able to determine what to do from reading their headers/source since I'm rather new to c++.
You can use MSGPACK_ADD_ENUM() macro. It has been supported since version 1.0.0. I recommend version 1.1.0 or later.
See:
https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_adaptor#enums
I applied MSGPACK_ADD_ENUM() to your code:
#ifndef LOG_MSG_HPP_
#define LOG_MSG_HPP_
#include <stdlib.h>
#include <msgpack.hpp>
/** #namespace logd */
namespace logd {
enum class log_level { SILENT,DEBUG };
enum class log_domain { AI, MISC };
class log_msg {
public:
log_msg(log_level lev, log_domain dom, std::string msg):lev_(lev), dom_(dom), msg_(msg) {}
log_level level() { return lev_;}
log_domain domain() { return dom_;}
std::string message() { return msg_; }
~log_msg() {}
MSGPACK_DEFINE(lev_, dom_, msg_);
log_msg() = default;
private:
log_level lev_ {log_level::DEBUG};
log_domain dom_ {log_domain::MISC};
std::string msg_ {"No message given."};
};
} /* namespace logd */
MSGPACK_ADD_ENUM(logd::log_level);
MSGPACK_ADD_ENUM(logd::log_domain);
#endif /* LOG_MSG_HPP_ */
#include <sstream>
#include <cassert>
int main() {
logd::log_msg lm { logd::log_level::SILENT, logd::log_domain::AI, "hello" };
std::stringstream ss;
msgpack::pack(ss, lm);
msgpack::object obj = msgpack::unpack(ss.str().data(), ss.str().size()).get();
logd::log_msg lm2 = obj.as<logd::log_msg>();
assert(lm.level() == lm2.level());
assert(lm.domain() == lm2.domain());
assert(lm.message() == lm2.message());
}
One way that seems to work is to wrap the enum in a union...
union log_level_t {
log_level lev;
int levint;
}
...
log_level_t lev_;
...
MSGPACK_DEFINE(lev_.levint, dom_.domint, msg);
This seems like a crappy approach, but it works.
I found another solution, maybe a little more elegant (ok, it's not so perfect):
MSGPACK_DEFINE((int&)lev_, (int&)dom_, msg)
So you can keep your original enum without creating a new union.
The deserialisation method should work without any problem.
Related
I have to read patient data from a .csv file and using a decision tree determine, based on the data being read in for each patient, whether the tumor is Benign or Malignant.
I am really struggling with how to even start this. So far I have written code that reads from the .csv file and stores the data into a vector as shown below spreading over a few header and cpp files.
From what I gather, I can create a parent decision class and then each attribute I am to process are the children classes. Not sure if that makes sense. Please let me know.
Below you will find the attributes I am to process along with a graphical tree that shows how it is determined whether the tumor is Benign or Malignant that I need to base my code off of. I will also include a small sample of the .csv file.
Please could I get some guidance as how I am to do this. I am having the greatest difficulty with pointer notation. Any guidance will greatly be appreciated.
CSVLine.h
#ifndef CSVLINE_H
#define CSVLINE_H
#include <string>
#include <sstream>
#include <vector>
using namespace std;
class CSVLine
{
private:
vector<string> data;
public:
CSVLine() {}
CSVLine(const CSVLine& other)
{
data = other.data;
}
CSVLine operator = (const CSVLine& other)
{
data = other.data;
}
~CSVLine() {}
void parse(string line, char delimiter = ',');
string getString(int columnNumber);
int getInt(int columnNumber);
};
#endif
CSVLine.cpp
#include "CSVLine.h"
void CSVLine::parse(string line, char delimiter)
{
stringstream inLine(line);
string tempColumn = "";
while (getline(inLine, tempColumn, delimiter))
{
data.push_back(tempColumn);
}
}
string CSVLine::getString(int columnNumber)
{
return data[columnNumber];
}
int CSVLine::getInt(int columnNumber)
{
return atoi(data[columnNumber].c_str());
}
CSVReader.h
#ifndef CSVREADER_H
#define CSVREADER_H
#include <vector>
#include <fstream>
#include <iostream>
#include "CSVLine.h"
using namespace std;
class CSVReader
{
public:
CSVReader() {}
vector<CSVLine> read(string fileName);
};
#endif
CSVReader.cpp
#include "CSVReader.h"
vector<CSVLine> CSVReader::read(string fileName)
{
ifstream inputFile;
vector<CSVLine> lines;
inputFile.open(fileName.c_str());
string line = "";
while (getline(inputFile, line))
{
CSVLine csvLine;
csvLine.parse(line);
lines.push_back(csvLine);
}
return lines;
}
Here is what I would do.
First, I would translate the table of features to a higher-order macro:
#define FOREACH_FEATURE(OP) \
OP(1, SampleCodeNumber, int, -1) \
OP(2, ClumpThickness, int, -1) \
OP(3, UniformityOfCellSize, int, -1)
// Fill in the rest of the table of features here yourself
Then I would use this macro to generate a struct with all the features of a patient like this:
struct PatientData {
#define DECL_FEATURE(index, name, type, init) type name = init;
FOREACH_FEATURE(DECL_FEATURE)
#undef DECL_FEATURE
PatientData() {}
PatientData(CSVLine& src) {
#define READ_FEATURE(index, name, type, init) name = src.getInt(index-1);
FOREACH_FEATURE(READ_FEATURE)
#undef READ_FEATURE
}
};
Then I would construct a PatientData object from a CSVLine:
CSVLine line = ...;
PatientData patientData(line);
Then I would implement the decision tree as nested if-statements on the patientData object:
if (patientData.UniformityOfCellSize <= 2) {
// ...
} else {
// ...
}
This would get you started but you need to complete and possible extend the FOREACH_FEATURE macro and implement the decision tree...
Nodes and pointers approach
If you don't want to implement your tree like above, ditch the above code and instead do the following. Start by including a few files that we need and implement a Feature class:
#include <memory>
#include <functional>
struct Feature {
int index1;
int apply(CSVLine& line) const {return line.getInt(index1-1);}
};
and translate the table of features to Feature like this:
Feature SampleCodeNumber{1};
Feature ClumpThickness{2};
Feature UniformityOfCellSize{3};
// Fill in the rest yourself
We are going to use an std::function<bool(CSVLine)> to decide for the branch in the tree:
typedef std::function<bool(CSVLine&)> BranchCondition;
Overloading the comparison operator for a Feature and double to return a BranchCondition lets us neatly express BranchConditions:
#define DEF_FEATURE_OP(op) BranchCondition operator op (Feature f, double x) {return [f, x](CSVLine& line) {return f.apply(line) op x;};}
DEF_FEATURE_OP(<)
DEF_FEATURE_OP(<=)
DEF_FEATURE_OP(>)
DEF_FEATURE_OP(>=)
#undef DEF_FEATURE_OP
We also need to declare the return value of the classification:
enum class Severity {
Benign, Malign
};
As a base class for the decision tree we declare
class PatientClassifier {
public:
virtual Severity classify(CSVLine& p) const = 0;
virtual ~PatientClassifier() {}
};
and implement it for the trivial case of a constant value along with a function severity to construct it:
class ConstantClassifier : public PatientClassifier {
public:
ConstantClassifier(Severity v) : _value(v) {}
Severity classify(CSVLine&) const override {return _value;}
private:
Severity _value;
};
std::shared_ptr<PatientClassifier> severity(Severity v) {
return std::make_shared<ConstantClassifier>(v);
}
and for the branching case along with a function branch:
class BranchingClassifier : public PatientClassifier {
public:
BranchingClassifier(
BranchCondition f,
const std::shared_ptr<PatientClassifier>& onTrue,
const std::shared_ptr<PatientClassifier>& onFalse)
: _f(f), _onTrue(onTrue), _onFalse(onFalse) {}
Severity classify(CSVLine& p) const override {
return (_f(p)? _onTrue : _onFalse)->classify(p);
}
private:
BranchCondition _f;
std::shared_ptr<PatientClassifier> _onTrue;
std::shared_ptr<PatientClassifier> _onFalse;
};
std::shared_ptr<PatientClassifier> branch(
BranchCondition f,
const std::shared_ptr<PatientClassifier>& onTrue,
const std::shared_ptr<PatientClassifier>& onFalse) {
return std::make_shared<BranchingClassifier>(f, onTrue, onFalse);
}
and then we just build the tree like
auto decisionTree = branch(
UniformityOfCellSize <= 2.0,
severity(Severity::Benign),
severity(Severity::Malign));
CSVLine line;
auto result = decisionTree->classify(line);
Note: You don't need custom copy constructor and assignment operator for CSVLine. And the getInt method could be marked as const.
I want to verify function parameters with gmock. Function parameters are in struct format as follows:
SomeClass.h:
class SomeClass {
public:
SomeClass();
virtual ~SomeClass();
struct SomeData {
int Value;
int Serie;
};
virtual void getData(SomeData &data);
};
SomeClass.cpp:
#include "../include/SomeClass.h"
SomeClass::SomeClass(){}
SomeClass::~SomeClass(){}
void SomeClass::getData(SomeData &data){
data.Serie = 4;
data.Value = 5;
}
MockSomeClass.h
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "../include/SomeClass.h"
class MockSomeClass: public SomeClass {
public:
MockSomeClass(){};
virtual ~MockSomeClass(){};
SomeData data;
MOCK_METHOD1(getData, void(SomeData &data));
};
testSomeClass:
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "../include/SomeClass.h"
#include "MockSomeClass.h"
using ::testing::_;
using ::testing::AtLeast;
TEST(testSomeMethod, test1){
MockSomeClass mock1;
SomeClass::SomeData data;
EXPECT_CALL(mock1, getData(_)).Times(AtLeast(2));
mock1.getData(data);
mock1.getData(data);
}
TEST(testSomeMethod, test2){
MockSomeClass mock2;
SomeClass::SomeData data;
EXPECT_CALL(mock2, getData(SomeClass::SomeData &data));
mock2.getData(data);
}
When I run testSomeClass, test1 goes through without errors but test2 is causing trouble. Any proposals how to include data in struct format to function call with gmock?
When writing googlemock expectation:
EXPECT_CALL(someMock, someMethod(arg1, arg2, arg3, ...))...;
The only allowed arguments (arg1, arg2, arg3) are matchers - ::testing::Matcher<T1> - where T1 is someMethod parameter types. Fortunately Matcher<T1> value can be constructed from T1 value.
So - this is not possible to use SomeClass::SomeData &data - because this is some argument declaration - not matcher value.
You probably wanted to use ::testing::Ref matcher - like:
EXPECT_CALL(mock2, getData(Ref(data)));
I have three files:
main.cpp
MyClass.cpp
MyClass.hpp
I have a library header file, "testLib.hpp", that I want to include in MyClass.hpp so that I can have one of testLib's objects be a class attribute.
I include MyClass.hpp in MyClass.cpp and in main.cpp. When attempting to compile the project, I get the following errors
MyClass.cpp multiple definition of 'testLib::testLib::function1()
obj/Release/main.o:main.cpp first defined here
MyClass.cpp multiple definition of 'testLib::testLib::function2()
obj/Release/main.o:main.cpp first defined here
and so on.
Both main.cpp and MyClass.cpp include MyClass.hpp (which includes testLib.hpp). Judging by the error, it looks like MyClass.cpp is attempting to include the library functions after they've already been included by main.cpp. However, I have include guards present in MyClass.hpp so I don't understand how it's trying to include MyClass.hpp twice.
Here's the code:
MyClass.hpp
#ifndef THIS_HEADER_H
#define THIS_HEADER_H
#include <stdint.h>
#include <iostream>
#include "testLib/testLib.hpp"
class MyClass
{
public:
void test();
int foo;
private:
uint32_t bar;
//I want to include an object from the library as part of this class
//TestLib::Device device;
};
#endif
MyClass.cpp
#include <stdio.h>
#include "MyClass.hpp"
void MyClass::test()
{
}
main.cpp
#include <iostream>
#include "MyClass.hpp"
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
Any help would be greatly appreciated!
EDIT
I tried to hide the actual filenames to make the question more general and clear, but it seems like the problem might be resulting from 'testLib.hpp', which I did not write. That file is actually the following "sweep.hpp" file. I got the 'multiple definition of/first defined here' errors for each of the public functions in this file:
sweep.hpp
#ifndef SWEEP_DC649F4E94D3_HPP
#define SWEEP_DC649F4E94D3_HPP
/*
* C++ Wrapper around the low-level primitives.
* Automatically handles resource management.
*
* sweep::sweep - device to interact with
* sweep::scan - a full scan returned by the device
* sweep::sample - a single sample in a full scan
*
* On error sweep::device_error gets thrown.
*/
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <vector>
#include <sweep/sweep.h>
namespace sweep {
// Error reporting
struct device_error final : std::runtime_error {
using base = std::runtime_error;
using base::base;
};
// Interface
struct sample {
const std::int32_t angle;
const std::int32_t distance;
const std::int32_t signal_strength;
};
struct scan {
std::vector<sample> samples;
};
class sweep {
public:
sweep(const char* port);
sweep(const char* port, std::int32_t bitrate);
void start_scanning();
void stop_scanning();
bool get_motor_ready();
std::int32_t get_motor_speed();
void set_motor_speed(std::int32_t speed);
std::int32_t get_sample_rate();
void set_sample_rate(std::int32_t speed);
scan get_scan();
void reset();
private:
std::unique_ptr<::sweep_device, decltype(&::sweep_device_destruct)> device;
};
// Implementation
namespace detail {
struct error_to_exception {
operator ::sweep_error_s*() { return &error; }
~error_to_exception() noexcept(false) {
if (error) {
device_error e{::sweep_error_message(error)};
::sweep_error_destruct(error);
throw e;
}
}
::sweep_error_s error = nullptr;
};
}
sweep::sweep(const char* port)
: device{::sweep_device_construct_simple(port, detail::error_to_exception{}), &::sweep_device_destruct} {}
sweep::sweep(const char* port, std::int32_t bitrate)
: device{::sweep_device_construct(port, bitrate, detail::error_to_exception{}), &::sweep_device_destruct} {}
void sweep::start_scanning() { ::sweep_device_start_scanning(device.get(), detail::error_to_exception{}); }
void sweep::stop_scanning() { ::sweep_device_stop_scanning(device.get(), detail::error_to_exception{}); }
bool sweep::get_motor_ready() { return ::sweep_device_get_motor_ready(device.get(), detail::error_to_exception{}); }
std::int32_t sweep::get_motor_speed() { return ::sweep_device_get_motor_speed(device.get(), detail::error_to_exception{}); }
void sweep::set_motor_speed(std::int32_t speed) {
::sweep_device_set_motor_speed(device.get(), speed, detail::error_to_exception{});
}
std::int32_t sweep::get_sample_rate() { return ::sweep_device_get_sample_rate(device.get(), detail::error_to_exception{}); }
void sweep::set_sample_rate(std::int32_t rate) {
::sweep_device_set_sample_rate(device.get(), rate, detail::error_to_exception{});
}
scan sweep::get_scan() {
using scan_owner = std::unique_ptr<::sweep_scan, decltype(&::sweep_scan_destruct)>;
scan_owner releasing_scan{::sweep_device_get_scan(device.get(), detail::error_to_exception{}), &::sweep_scan_destruct};
auto num_samples = ::sweep_scan_get_number_of_samples(releasing_scan.get());
scan result;
result.samples.reserve(num_samples);
for (std::int32_t n = 0; n < num_samples; ++n) {
auto angle = ::sweep_scan_get_angle(releasing_scan.get(), n);
auto distance = ::sweep_scan_get_distance(releasing_scan.get(), n);
auto signal = ::sweep_scan_get_signal_strength(releasing_scan.get(), n);
result.samples.push_back(sample{angle, distance, signal});
}
return result;
}
void sweep::reset() { ::sweep_device_reset(device.get(), detail::error_to_exception{}); }
} // ns
#endif
A simplified version of your problem:
buggy.hpp
int function() { return 0; }
main.cpp
#include "buggy.hpp"
int main() { return 0; }
other.cpp
#include "buggy.hpp"
The problem is that buggy.hpp is defining function, not just declaring. Once the header inclusion is expanded, that means function is declared in both main.cpp and other.cpp - and that is not allowed.
The fix is to declare function as inline which allows the function to be declared in multiple translation units.
inline int function() { return 0; }
In fact, allowing multiple definitions is the only meaning of inline to the C++ standard. Compilers may treat it as a hint that the function body may be expanded inline. Good ones won't; they are better at making that sort of decision that programmers).
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 just moved from C to C++, and now work with lists.
I have a class called "message", and I need to have a class called "line",
which should have a list of messages in its properties. as I learned, the object's properties should be initialized in the constructor's initialization list, and i had the "urge" to initialize the messages list in addition to the rest of the properties (some strings and doubles). is that "urge" justified? does the list need to be initialized?
here is my code.
the purpose is to create an empty list of lines, and the constructor I'm talking about is the one in line.cpp
//-------------------
//Code for line.h:
//-------------------
#ifndef LINE_H_
#define LINE_H_
#include "message.h"
#include <string>
#include <list>
using namespace std;
namespace test
{
using std::string;
class Line
{
public:
// constractor with parameters
Line(const string& phoneStr, double callRate, double messageRate);
//function to get phone string
string getPhoneStr() const;
double getCallRate() const;
double getMessageRate() const;
double getLastBill() const;
void addMessage(const string& phoneStr);
private:
string mPhoneStr;
list<Message> mMessages;
double mMessageRate;
double mLastBill;
};
}
#endif /* LINE_H_ */
//-------------------
//Code for line.cpp:
//-------------------
#include "line.h"
namespace test
{
Line::Line(const string& phoneStr, double callRate, double messageRate)
: mPhoneStr(phoneStr), mCallRate(callRate), mMessageRate(messageRate),
mLastBill(0) {}
//getters:
string Line::getPhoneStr() const
{
return mPhoneStr;
}
double Line::getCallRate() const
{
return mCallRate;
}
double Line::getMessageRate() const
{
return mMessageRate;
}
double Line::getLastBill() const
{
return mLastBill;
}
}
//-------------------
//Code for message.h:
//-------------------
#ifndef MESSAGE_H_
#define MESSAGE_H_
#include <string>
namespace test
{
using std::string;
class Message
{
public:
// constractor with parameters
Message(const string& phoneStr);
//function to get phone string
string getPhoneStr() const;
//function to set new phone string
void setPhoneStr(const string& phoneStr);
private:
string mPhoneStr;
};
}
#endif /* MESSAGE_H_ */
//-----------------------------------------------------------------------
//---------------------
//code for message.cpp:
//---------------------
#include "message.h"
namespace test
{
Message::Message(const string& phoneStr) : mPhoneStr(phoneStr) {}
string Message::getPhoneStr() const
{
return mPhoneStr;
}
void Message::setPhoneStr(const string& phoneStr)
{
mPhoneStr = phoneStr;
}
}
The initialization list is for initializing any base classes and member variables. The body of the constructor is meant to run any other code that you need before the object can be considered initialized.
I'm having a hard time understanding your situation, but hopefully the above helps.
You don't have to do everything in the initialisation list. It's hard to tell without seeing some code, but it sounds like adding the messages would be better done in the body of the constructor.