I have a problem related to the parsing of a JSON file. It is composed of an array of JSON objects and the program I've developed parses this by just iterate over the various { ... }
elements found in the file.
The elements (in the JSON file, these are the rows of the file) are structured in such a way that inside each row there can be other inner elements, but this goes beyond the problem I have to handle now.
In short, I have a Block
; inside it, I have, in addition to other fields, a vector Operations
whose type is Operation
, and in this vector, each element can be a subtype of Operation
, so e.g., Vote
, Comment
, Follow
, Repost
, ...
To handle this, I've created a BlockStructure.hpp
file header that includes both the definition of the class Operation
together with the (only) virtual method print()
and the definition of the classes listed before. In addition to these definitions, there is also the definition of the TransactionBlock
, Transaction
and BlockStructure
// BlockStructure.hpp
#include <string>
#include <list>
#include <utility>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
using ui = unsigned int;
class Operation{
public:
string kind = "";
virtual string print();
};
class Vote : public Operation {
private:
string voter, author;
public:
Vote(string voter, string author);
virtual string print();
string get_voter();
string get_author();
};
class Comment : Operation{
private:
string parent_author, author, title, body;
public:
Comment(string parent_author, string author, string title, string body);
string get_author();
virtual string print();
};
class Follow : Operation{
private:
string follower, following;
vector<string> common_follows{};
public:
CustomJsonFollow(string follower, string following, vector<string> common_follows);
static void strip(vector<string> *w); // implements the common strip function
static void stripLeadingAndtrailingSpaces(string *stng); // implements the strip by removing the whitespaces
string get_follower();
string get_following();
vector<string> get_common_follows();
virtual string print();
};
class Reblog : Operation{
private:
string account, author;
public:
CustomJsonReblog(string account, string author);
string get_account();
string get_author();
virtual string print();
};
class TransactionBlock{
private:
vector<Operation*> operations{};
string transaction_id;
int transaction_num = -1;
public:
TransactionBlock(vector<Operation*> ops);
TransactionBlock();
vector<Operation*> get_operations();
void set_operations(vector<Operation*> ops);
string get_transaction_id();
void set_transaction_id(string tid);
int get_transaction_num();
void set_transaction_num(int trn);
string print();
};
class Transaction{
private:
string transaction_id;
TransactionBlock transactionBlock;
public:
Transaction(string tr_id, TransactionBlock transactionBlock);
string get_transaction_id();
void set_transaction_id(string tid);
TransactionBlock get_transaction_block();
void set_transaction_block(TransactionBlock tbl);
void print();
};
class BlockStructure{
private:
list<Transaction> transactions{};
string timestamp;
list<int> transaction_ids;
TransactionBlock transactionBlock;
public:
BlockStructure();
void set_transactions(list<Transaction> transact);
void set_transaction_block(TransactionBlock transactBlock);
void set_timestamp(string ts);
void set_transaction_ids(list<int> tr_ids);
void set_transaction_block(const TransactionBlock& tb);
TransactionBlock get_transaction_block();
void print_block();
};
Afterwards, in another file, called BlockStructure.cpp
I have the implementation of all the methods defined in the header, so:
// BlockStructure.cpp
#include "BlockStructure.hpp"
using namespace std;
using ui = unsigned int;
// Operation
string Operation::print(){
return "";
}
// Vote
Vote::Vote(string voter, string author) : voter(move(voter)), author(move(author)) { this->kind = "VOTE"; }
string Vote::print(){
string toret = "V\n";
toret += "voter: " + this->voter + "\n";
toret += "auth: " + this->author + "\n";
return toret;
}
string Vote::get_voter(){
return this->voter;
}
string Vote::get_author(){
return this->author;
}
// Comment
Comment::Comment(string parent_author, string author, string title, string body){
this->parent_author = move(parent_author);
this->author = move(author);
this->title = move(title);
this->body = move(body);
this->kind = "COMMENT";
}
string Comment::get_author(){
return this->author;
}
string Comment::print(){
string toret = "C\n";
toret += "par_auth: " + this->parent_author + "\n";
toret += "auth: " + this->author + "\n";
toret += "title: " + this->title + "\n";
toret += "body: " + this->body + "\n";
return toret;
}
// Follow
void Follow::strip(vector<string> *w) {
auto it = w->begin();
for (ui i = 0; i < w->size(); i++) {
stripLeadingAndtrailingSpaces(&(*it));
advance(it, 1);
}
}
void Follow::stripLeadingAndtrailingSpaces(string *stng) {
char *cur = const_cast<char *>(stng->c_str());
/* First remove leading spaces */
const char *firstNonSpace = cur;
while (*firstNonSpace != '\0' && isspace(*firstNonSpace))
++firstNonSpace;
size_t len = strlen(firstNonSpace) + 1;
memmove(cur, firstNonSpace, len);
/* Now remove trailing spaces */
char *endOfString = cur + len;
while (cur < endOfString && isspace(*endOfString))
--endOfString;
*endOfString = '\0';
}
Follow::Follow(string follower, string following, vector<string> common_follows, vector<string> required_posting_auths) {
this->follower = move(follower);
stripLeadingAndtrailingSpaces(&this->follower);
this->following = move(following);
stripLeadingAndtrailingSpaces(&this->following);
this->common_follows = move(common_follows);
strip(&this->common_follows);
this->kind = "FOLLOW";
}
string Follow::get_follower() {
return this->follower;
}
string Follow::get_following(){
return this->following;
}
vector<string> Follow::get_common_follows(){
return this->common_follows;
}
string Follow::print(){
string toret = "F\n";
toret += "follower: " + this->follower + "\n";
toret += "following: " + this->following + "\n";
if (!this->common_follows.empty()){
toret += "common_follows: \n";
for (const string& w : this->common_follows)
toret += w + "\n";
} else
toret += "common_follows: []\n";
return toret;
}
// Reblog
Reblog::Reblog(string account, string author){
this->account = move(account);
this->author = move(author);
this->kind = "REBLOG";
}
string Reblog::get_account(){
return this->account;
}
string Reblog::get_author(){
return this->author;
}
string Reblog::print(){
string toret = "RB\n";
toret += "account: " + this->account + "\n";
toret += "author: " + this->author + "\n";
return toret;
}
(and similarly for the TransactionBlock
, Transaction
and BlockStructure
)
Then, I include BlockStructure.hpp
inside the file ParsingBlocks.cpp
defined in the following. The
"jsoncpp/dist/json/json.h"
"jsoncpp/dist/jsoncpp.cpp"
files that I include in the beginning are the result of the python amalgamate.py
script (this script generates also the jsoncpp/dist/json/json-forward.h
, but I have read that it is not commonly used for the projects). I read that it is an approach to integrating JsonCpp in the project.
#include <fstream>
#include "jsoncpp/dist/json/json.h"
#include "jsoncpp/dist/jsoncpp.cpp"
#include "BlockStructure.hpp"
class ParsingBlocks {
private:
string path;
public:
ParsingBlocks(string path) : path(move(path)) {}
vector<Operation*> fill_ops(const Value& trans_operations){
vector<Operation*> op_array = {};
for(Value op : trans_operations) {
if (op[(UInt) 0].asString() == "vote") {
string voter = op[1]["voter"].asString();
Vote vt = Vote(op[1]["voter"].asString(), op[1]["author"].asString());
op_array.push_back((Operation*)&vt);
} else if (op[(UInt) 0].asString() == "comment") {
Comment cmt = Comment(op[1]["parent_author"].asString(),
op[1]["author"].asString(),
op[1]["title"].asString(),
op[1]["body"].asString());
op_array.push_back((Operation*)&cmt);
} else if (op[(UInt) 0].asString() == "custom_json") {
auto custom = op[(UInt) 1];
if (custom["id"].asString() == "follow") {
Value injs = custom["id"];
if (injs[(UInt) 0].asString() == "follow") {
vector<string> common_follows = {};
for (const auto &itr : injs[(UInt) 1]["common_follows"])
common_follows.push_back(itr.asString());
CustomJsonFollow follow = CustomJsonFollow(
injs[1]["follower"].asCString(),
injs[1]["following"].asCString(),
common_follows);
op_array.push_back((Operation*)&follow);
} else {
if (injs[(UInt) 0].asString() == "reblog") {
CustomJsonReblog reblog = CustomJsonReblog(
injs[(UInt) 1]["account"].asCString(),
injs[(UInt) 1]["author"].asCString());
op_array.push_back((Operation*)&reblog);
}
}
}
}
}
return op_array;
BlockStructure evaluate_block(const string& line){
ifstream ifs(line);
CharReaderBuilder reader;
Value data;
string errs;
Json::parseFromStream(reader, ifs, &data, &errs);
BlockStructure bs = BlockStructure();
for(Value::ArrayIndex i = 0; i != data.size(); i++){
bs.set_timestamp(data[i]["timestamp"].asString());
list<Transaction> transss{};
for(const Value& transaction : data[i]["transactions"]){
vector<Operation*> operations = fill_ops(transaction["operations"]);
auto t_block = new TransactionBlock();
t_block->set_transaction_num(transaction["transaction_num"].asInt());
if (!operations.empty()){
t_block->set_operations(operations);
}
Transaction t = Transaction(transaction["transaction_id"].asString(), *t_block);
transss.push_back(t);
}
bs.set_transactions(transss);
bs.set_witness_name(data[i]["witness"].asString());
}
return bs;
}
list<BlockStructure> parsing_blocks(){
string line;
ifstream file;
file.open(this->path);
list<BlockStructure> blocks{};
if(!file.is_open()){
perror("Error open");
exit(EXIT_FAILURE);
}
while (getline(file, line)){
BlockStructure b = evaluate_block(line);
blocks.push_back(b);
}
return blocks;
}
}
Leaving aside possible copy-and-paste errors (which I don't think I've done), now, I use the ParsingBlocks.cpp
into the main.cpp
defined as follows:
#include "ParsingBlocks.cpp"
using namespace std;
int main() {
ParsingBlocks *pb = new ParsingBlocks("jsonfile.json");
list<BlockStructure> blocks = pb->parsing_blocks();
for(BlockStructure bl : blocks){
bl.print_block();
}
return 0;
}
I don't show another class (UserStructure.cpp
) used in the compilation because it is useless for the purposes of the question.
My problem now is to understand how to compile the main.cpp
file.
I'm relying on the jsoncpp
external library so, at compile time I execute from command line the following:
c++ -std=c++17 -Wall -pedantic -I. .\UserStructure.cpp .\BlockStructure.hpp .\BlockStructure.cpp .\ParsingBlocks.cpp .\main.cpp -o parsing.out
where the -I.
flag is used to link in the current directory, the jsoncpp.cpp
and json/json.h
defined in the #include
s at the beginning of the ParsingBlock.cpp
file, as shown before.
The compilation gives me the following errors:
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x2b6): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x2e0): multiple definition of `Json::Features::all()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x2e0): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x2fe): multiple definition of `Json::Features::strictMode()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x2fe): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x32c): multiple definition of `Json::Reader::containsNewLine(char const*, char const*)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x32c): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x36e): multiple definition of `Json::Reader::Reader()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x36e): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x36e): multiple definition of `Json::Reader::Reader()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x36e): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x44a): multiple definition of `Json::Reader::Reader(Json::Features const&)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x44a): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x44a): multiple definition of `Json::Reader::Reader(Json::Features const&)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x44a): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x52a): multiple definition of `Json::Reader::parse(std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> > const&, Json::Value&, bool)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x52a): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x5de): multiple definition of `Json::Reader::parse(std::istream&, Json::Value&, bool)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x5de): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x698): multiple definition of `Json::Reader::parse(char const*, char const*, Json::Value&, bool)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x698): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x934): multiple definition of `Json::Reader::readValue()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x934): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x1032): multiple definition of `Json::Reader::skipCommentTokens(Json::Reader::Token&)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x1032): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x1086): multiple definition of `Json::Reader::readToken(Json::Reader::Token&)'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x1086): first defined here
C:\Users\Utente\AppData\Local\Temp\cc8uGWcY.o:main.cpp:(.text+0x1246): multiple definition of `Json::Reader::skipSpaces()'
C:\Users\Utente\AppData\Local\Temp\ccDGeveZ.o:ParsingBlocks.cpp:(.text+0x1246): first defined here
So, it should be evident that the problem is in the linking phase, but I cannot solve it!
I have tried by reading both the documentation and other questions on stackoverflow.com, but I get nowhere.
Thanks in advance for the help, and I'm sorry if this question is a little bit huge.
EDIT: The stderr output does not stop there: there are at least 1000 lines of error, but each one of these regards the same multiple definition of ..., C:...: first defined here
.
You keep #include
ing source files ("ParsingBlocks.cpp" and "jsoncpp/dist/jsoncpp.cpp").
Don't do that.
It results in the definitions existing multiple times across your final program.
Your main.cpp should #include "ParsingBlocks.h"
so that it has access to the declarations of things.
And you should compile and link jsoncpp/dist/jsoncpp.cpp
into your executable in your build tool, just like your other cpp
files:
c++ -std=c++17 -Wall -pedantic -I. \
UserStructure.cpp \
BlockStructure.cpp \
ParsingBlocks.cpp \
main.cpp \
jsoncpp/dist/jsoncpp.cpp \
-o parsing.out
The backslashed-newlines are for clarity so we can more easily read the command.
Notice that I also removed your attempt to link in header files.
Refer to your C++ book for more information on how to build programs.