使用jsoncpp库时的链接问题

Linking problem when using jsoncpp library

我在解析 JSON 文件时遇到问题。它由 JSON objects 数组组成,我开发的程序通过迭代文件中找到的各种 { ... } 元素来解析它。

元素(在 JSON 文件中,这些是文件的行)的结构使得每一行内可以有其他内部元素,但这超出了我遇到的问题现在处理。

总之,我有一个Block;在其中,除了其他字段外,我还有一个类型为 Operation 的向量 Operations,在该向量中,每个元素都可以是 Operation 的子类型,因此例如 VoteCommentFollowRepost...

为了处理这个问题,我创建了一个 BlockStructure.hpp 文件 header,其中包含 class Operation 的定义以及(唯一的)虚拟方法print() 和前面列出的 classes 的定义。除了这些定义之外,还有TransactionBlockTransactionBlockStructure

的定义
// 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 的文件中,我实现了 header 中定义的所有方法,因此:

// 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 != '[=11=]' && 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 = '[=11=]';
}

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

TransactionBlockTransactionBlockStructure 也类似)


然后,我将 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;
    }

}

抛开可能的 copy-and-paste 错误(我认为我没有做过),现在,我使用 ParsingBlocks.cppmain.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;
}

我没有展示编译中使用的另一个 class (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.标志用于当前目录中的link,jsoncpp.cppjson/json.h开头定义的#include 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

所以,应该很明显问题在linking阶段,但我无法解决!

我已经尝试阅读 whosebug.com 上的文档和其他问题,但我一无所获。

在此先感谢您的帮助,如果这个问题有点大,我很抱歉。

编辑:stderr 输出不止于此:至少有 1000 行错误,但每一行都认为相同 multiple definition of ..., C:...: first defined here.

在主文件而不是源文件中包含头文件:

#include "ParsingBlocks.hpp"
C 和 C++ 中的

#include 不像许多其他语言那样是基于模块的包含,而是直接插入文件的文本。这意味着 ParsingBlocks.cpp 中的所有内容都被编译器读取了两次:一次是在编译主文件时,一次是在编译 ParsingBlocks.cpp 本身时,因此是重复项。

此外,您不需要在编译器命令行中指定头文件。

您刚刚包含了一个 cpp 文件。只应包含头文件。 通过包含 cpp 文件,编译器试图多次定义方法。 这基本上就是头文件存在的原因。

您保留 #includeing 源文件("ParsingBlocks.cpp" 和 "jsoncpp/dist/jsoncpp.cpp")。

不要那样做。

它导致定义在你的最终程序中存在多次。

您的 main.cpp 应该 #include "ParsingBlocks.h" 以便它可以访问事物的 声明

并且您应该 编译并 link 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

反斜线换行是为了清晰起见,这样我们可以更轻松地阅读命令。

请注意,我还删除了您在头文件中对 link 的尝试。

有关如何构建程序的更多信息,请参阅您的 C++ 书籍。