Проблема с соединением при использовании библиотеки jsoncpp - PullRequest
0 голосов
/ 18 февраля 2020

У меня есть проблема, связанная с анализом файла JSON. Он состоит из массива JSON объектов, и разработанная мною программа анализирует его, просто перебирая различные { ... } элементы, найденные в файле.

Элементы (в файле JSON (это строки файла) структурированы таким образом, что внутри каждой строки могут быть другие внутренние элементы, но это выходит за рамки проблемы, с которой мне приходится сталкиваться.

Короче говоря, у меня есть Block; внутри него у меня есть, помимо других полей, вектор Operations, тип которого Operation, и в этом векторе каждый элемент может иметь подтип Operation, например, Vote, Comment , Follow, Repost, ...

Для этого я создал заголовок файла BlockStructure.hpp, который включает определение класса Operation вместе с (только) виртуальным метод print() и определение классов, перечисленных ранее. В дополнение к этим определениям, есть также определение TransactionBlock, Transaction и 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();

};

Впоследствии, в другом файле, называемом BlockStructure.cpp, у меня есть реализация всех методы, определенные в заголовке, так:

// 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;
}

(и аналогично для TransactionBlock, Transaction и BlockStructure)


Затем я включаю BlockStructure.hpp внутри файла ParsingBlocks.cpp, определенного в следующем. Файлы

"jsoncpp/dist/json/json.h"
"jsoncpp/dist/jsoncpp.cpp"

, которые я включаю в начале, являются результатом сценария python amalgamate.py (этот сценарий также генерирует jsoncpp/dist/json/json-forward.h, но я читал, что он обычно не используется для проектов) , Я читал, что это подход к интеграции JsonCpp в проект.

#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;
    }

}

Оставляя в стороне возможные ошибки копирования и вставки (что, я думаю, я не сделал), теперь я использую ParsingBlocks.cpp в main.cpp, определенном следующим образом:

#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;
}

Я не показываю другой класс (UserStructure.cpp), используемый в компиляции, потому что он бесполезен для целей вопрос.

Теперь моя проблема - понять, как скомпилировать файл main.cpp.

Я полагаюсь на внешнюю библиотеку jsoncpp, поэтому во время компиляции выполняю из команды введите следующую строку:

c++ -std=c++17 -Wall -pedantic -I. .\UserStructure.cpp .\BlockStructure.hpp .\BlockStructure.cpp .\ParsingBlocks.cpp .\main.cpp -o parsing.out

, где флаг -I. используется для связи в текущем каталоге, jsoncpp.cpp и json/json.h, определенные в #include s в начало файла ParsingBlock.cpp, как показано выше.

Компиляция выдает мне следующие ошибки:

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

Итак, должно быть очевидно, что проблема в фазе компоновки , но я не могу решить это!

Я попытался, прочитав и документацию, и другие вопросы. o n stackoverflow.com, но я ничего не получаю.

Заранее спасибо за помощь, и я извиняюсь, если этот вопрос немного огромен.

РЕДАКТИРОВАТЬ: вывод stderr не остановитесь на этом: есть по крайней мере 1000 строк ошибок, но каждая из них относится к одному и тому же multiple definition of ..., C:...: first defined here.

Ответы [ 3 ]

2 голосов
/ 18 февраля 2020

Вы сохраняете #include исходные файлы ("ParsingBlocks. cpp" и "jsoncpp / dist / jsoncpp. cpp").

Не делайте этого.

Это приводит к определениям , существующим несколько раз в вашей окончательной программе.

Ваша основная. cpp должна #include "ParsingBlocks.h", чтобы иметь доступ к декларации вещей.

И вы должны скомпилировать и связать jsoncpp/dist/jsoncpp.cpp в свой исполняемый файл в инструменте сборки, так же как и в других cpp файлах:

c++ -std=c++17 -Wall -pedantic -I. \
  UserStructure.cpp \
  BlockStructure.cpp \
  ParsingBlocks.cpp \
  main.cpp \
  jsoncpp/dist/jsoncpp.cpp \
  -o parsing.out

Символы обратной косой черты предназначены для ясности, поэтому мы можем легче прочитать команду.

Обратите внимание, что я также удалил вашу попытку связать файлы заголовков.

Обратитесь к вашему C ++ книга для получения дополнительной информации о том, как создавать программы.

1 голос
/ 18 февраля 2020

вы только что включили cpp файл. должны быть включены только заголовочные файлы. включив файлы cpp, компилятор пытается определить методы несколько раз. это в основном причина, по которой существуют заголовочные файлы.

1 голос
/ 18 февраля 2020

Включите заголовочный файл в ваш основной файл вместо исходного:

#include "ParsingBlocks.hpp"

#include в C, и C ++ не является модульным включением, как во многих других языках, но просто непосредственно вставляет текст файла. Это означает, что все из ParsingBlocks.cpp было прочитано компилятором дважды: один раз, когда он скомпилировал ваш основной файл, и один раз, когда он сам скомпилировал ParsingBlocks.cpp, следовательно, дубликаты.

Также вам не нужно указывать файлы заголовков в командной строке вашего компилятора.

...