创建仅模板标题项目库文件

时间:2015-04-30 18:22:34

标签: c++ templates c-preprocessor

我的C ++项目完全基于模板,因此我的代码分为不同的头文件(仅限标题的项目)。 但是对于库的用户,我想提供一个头文件(&#34;库文件&#34;),他们必须包括使用库。 一种可能性是手动将所有代码复制到一个文件中,但如果项目较大,我想用make自动创建文件。所以我的想法是设置库文件,我在其中包含所有源文件并使用g++ -E(只是预处理)处理它。但是预处理器无论如何都会将包含在多个文件(即<string>)中的内容包含在输出文件中。这导致了多重定义&#39;我使用该库时出错。

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

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

示例:

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,则会多次定义符号(即deque标头的一部分)。

2 个答案:

答案 0 :(得分:1)

如果您的库只是标题,则必须将所有非模板函数(包括特化)标记为inline。否则,在多个TU中包含一个标题会导致多个定义链接错误。

示例:

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

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

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

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

答案 1 :(得分:1)

不要将所有头文件的内容复制到一个巨型头文件中,而应考虑使用#include语句。

mylibrary.h:

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