创建模板header-only项目库文件

Create a template header-only project library file

我的 C++ 项目完全基于模板,因此我的代码分为不同的 header 文件(header-only 项目)。 但是对于库的用户,我想提供一个 header 文件("library file"),他们必须包含才能使用库。 一种可能是手动将所有代码复制到一个文件中,但如果项目更大,我想使用 make 自动创建文件。所以我的想法是设置库文件,我在其中包含所有源文件并使用 g++ -E 处理它(只是预处理)。但是预处理器无论如何都会将内容包含的文件写入多个文件(即 <string>),也多次写入输出文件。当我使用库时,这会导致 'multiple definition' 错误。

那么是否有可能阻止多次复制,或者是否有另一种方法可以用来实现我获取一个库文件的目标?

如果您需要一些示例代码来回答问题,请发表评论。

示例:

header.h:

#ifndef CSV_H_
#define CSV_H_

#include <deque>
#include <istream>
#include <string>

using std::deque;
using std::istream;
using std::string;

namespace csv {

template<typename T>
class csv_parser {
private:
    deque<T> line;
public:
    template<typename S>
    friend csv_parser<S>& operator>> (istream& input, csv_parser<S>& parser); //file input stream operator
    deque<T>& operator>> (deque<T>& target); //data output operator
    deque<T> get_line(); //get parsed line
    void set_line(string input);  //set line and parse
};
#endif

definition.h:

#include <deque>
#include <istream>
#include <sstream>
#include <string>
#include <algorithm>
#include "../headers/csv.h"

using std::deque;
using std::istream;
using std::stringstream;
using std::string;
using std::getline;
using std::copy;

namespace csv {

template<typename T>
csv_parser<T>& operator>> (istream& input, csv_parser<T>& parser) {
    parser.line.clear(); //clear the data
    T buffer;
    string line;
    stringstream converter;

    getline(input, line); //get one line from csv file

    while (line.size() > 0) { //get field from csv line and delete this segment until
                              //input line is empty
        if (line.find_first_of(",") != -1) { //(not the last segment [one ',' left])
            converter << line.substr(0, line.find_first_of(",")); //add segment to converter
            converter >> buffer; //convert segment to T type
            converter.clear(); //clear flags of converter (normally EOF flag is set
                                //after converting), so writing in converter is enabled again
            parser.line.push_back(buffer); //write segment into data
            line.erase(0, line.find_first_of(",")+1); //delete segment from input line
        }
        else { //(last segment in line)
            converter << line.substr(0, line.length()); //get rest of the line
            converter >> buffer; //convert segment to T type
            converter.clear(); //clear flags of converter (normally EOF flag is set
                               //after converting), so writing in converter is enabled again
            parser.line.push_back(buffer);//write segment into data
            line.erase(0, line.length()); //delete rest of input string
        }
    }
    return parser;
}

template<typename T>
void csv_parser<T>::set_line(string input) {
    line.clear();
    T buffer;
    stringstream converter;

    while (input.size() > 0) { //get field from input and delete this segment until
                              //input is empty
        if (input.find_first_of(",") != -1) { //(not the last segment [one ',' left])
            converter << input.substr(0, input.find_first_of(",")); //add segment to converter
            converter >> buffer; //convert segment to T type
            converter.clear(); //clear flags of converter (normally EOF flag is set
                                //after converting), so writing in converter is enabled again
            line.push_back(buffer); //write segment into data
            input.erase(0, input.find_first_of(",")+1); //delete segment from input
        }
        else { //(last segment in line)
            converter << input.substr(0, input.length()); //get rest of the input
            converter >> buffer; //convert segment to T type
            converter.clear(); //clear flags of converter (normally EOF flag is set
                               //after converting), so writing in converter is enabled again
            line.push_back(buffer);//write segment into data
            input.erase(0, input.length()); //delete rest of input
        }
    }
}

template<typename T>
deque<T>& csv_parser<T>::operator>>(deque<T>& target) {//write parsed data into target
    target.clear();
    target.assign(line.begin(), line.end()); //copy data into target
    line.clear(); //delete data
    return target;
}

template<typename T>
deque<T> csv_parser<T>::get_line(){ //return data
    deque<T> buffer = line; //copy data into buffer
    line.clear(); //delete data
    return buffer; //return buffer
}

}

(未编译)library.h:

#include "header.h"
#include "definition.h"

编译器指令:

g++ -E library.h -o library_out.h

因此,如果我在应用程序中使用 library_out.h,作为双端队列 header 一部分的符号将被定义多次。

如果您的图书馆只有 header,您必须将所有 non-template 函数(包括专业)标记为 inline。否则,在多个 TU 中包含 header 会导致多个定义链接错误。

示例:

// header.h
inline void f(){}

// p1.cpp
#include "header.h"

// p2.cpp
#include "header.h"

现在编译 p1.cppp2.cpp 没问题,不再有符号重复错误。

与其将所有头文件的内容复制到一个巨大的头文件中,不如考虑使用 #include 语句。

mylibrary.h:

#include "myfile1.h"
#include "myfile2.h"
#include "myfile3.h"