创建模板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.cpp
和 p2.cpp
没问题,不再有符号重复错误。
与其将所有头文件的内容复制到一个巨大的头文件中,不如考虑使用 #include
语句。
mylibrary.h:
#include "myfile1.h"
#include "myfile2.h"
#include "myfile3.h"
我的 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.cpp
和 p2.cpp
没问题,不再有符号重复错误。
与其将所有头文件的内容复制到一个巨大的头文件中,不如考虑使用 #include
语句。
mylibrary.h:
#include "myfile1.h"
#include "myfile2.h"
#include "myfile3.h"