[转贴]How To Organize Template Source Code

How To Organize Template Source Code
By Nemanja Trifunovic
Different ways to organize source code in C++ template libraries

Introduction
Often I get asked whether programming with templates is hard or easy. The answer I usually give is: "It is easy to use templates, but it is hard to make them". Just take a look at some template libraries that we use in our everyday programming, like STL, ATL, WTL, some libraries from Boost, and you will see what I mean by this. Those libraries are great example of the principle "simple interface - complex implementation".

I started using templates five years ago when I discovered MFC template containers, and until last year I had no need to develop them myself. When I finally got to the point that I needed to develop some template classes, the first thing that hit me was the fact that the "traditional" way of organizing source code (declarations in *.h files, and definitions in *.cpp files) does not work with templates. It took me some time to understand why this is the case, and how to work around this problem.

This article is aimed at developers who understand templates well enough to use them, but are not very experienced at developing them. Here, I will cover only template classes and not template functions, but the principles are the same in both cases.

The Problem Described
To illustrate the problem, we will use an example. Suppose we have a template class array (nothing to do with boost::array template class) in a file array.h.

<script src="fi_divexpand.js" type="text"></script>
// array.h
template
class array
{
? ? T data_[SIZE];
? ? array (const array& other);
? ? const array& operator = (const array& other);
public:
? ? array(){};
? ? T& operator[](int i) {return data_[i];}
? ? const T& get_elem (int i) const {return data_[i];}
? ? void set_elem(int i, const T& value) {data_[i] = value;}
? ? operator T*() {return data_;}? ? ?
};
? ? ? ? ? ?


Also, we have a file main.cpp in which is the code that uses array:

<script src="fi_divexpand.js" type="text"></script>
// main.cpp
#include "array.h"

int main(void)
{
array intArray;
intArray.set_elem(0, 2);
int firstElem = intArray.get_elem(0);
int* begin = intArray;
}


This compiles fine, and does exactly what we want: first we make an array of 50 integers, then set the first element to be 2, read the first element, and finally take the pointer to the beginning of the array.

Now, what happens if we try to organize the code in more traditional way? Let's try to split the code in array.h and see what happens. Now we have two files: array.h and array.cpp (main.cpp remains unchanged).

<script src="fi_divexpand.js" type="text"></script>
// array.h? ? ? ?
template
class array
{
? ? ? T data_[SIZE];
? ? ? array (const array& other);
? ? ? const array& operator = (const array& other);
? public:
? ? ? array(){};
? ? ? T& operator[](int i);
? ? ? const T& get_elem (int i) const;
? ? ? void set_elem(int i, const T& value);
? ? ? operator T*();? ? ?
};? ? ? ?
? ?
// array.cpp
#include "array.h"

template
? ? ? ?T& array ::operator [](int i)
? ? {
? ? return data_[i];
? ? }

template
? ? ? ?const T& array ::get_elem(int i) const
? ? {
? ? return data_[i];
? ? }

template
? ? ? ?void array ::set_elem(int i, const T& value)
? ? {
? ? data_[i] = value;
? ? }
template array ::operator T*()
? ? {
? ? return data_;
? ? }



Try to compile this, and you will get three linker errors. The questions are:

1.Why are these errors reported in the first place?
2.Why there are only three linker errors? We have four member functions in array.cpp.

To answer these questions, we will need to dig into a little more details about the template instantiation process.

Template Instantiation
One of the mistakes programmers usually make when they work with template classes is to treat them as types. The term parameterized types which is often used for template classes certainly does lead us to think this way. Well, template classes are not types, they are just what the name suggests: templates. There are several important concepts to understand about the relation between template classes and types:

<script src="fi_divexpand.js" type="text"></script>
1.Compiler uses template classes to create types by substituting template parameters, and this process is called instantiation.
2.The type that is created from a template class is called a? specialization.
3.Template instantiation happens on-demand, which means that the compiler will create the specialization when it finds its use in code (this place is called point of instantiation).
4.To create a specialization, compiler will need to "see" not only the declaration of the template in the point of instantiation, but also the definition.
5.Template instantiation is lazy, which means that only the definitions of functions that are used are instantiated.


If we go back to our example, array is a template, and array is a template specialization - a type. The process of creating array from array is instantiation. The point of instantiation is in the file main.cpp. If we organize the code in the "traditional" way, compiler will see the declaration of the template (array.h), but not the definition (array.cpp ). Therefore, compiler will not be able to generate the type array . However, it will not report an error: it will assume that this type is defined in some other compilation unit, and leave it to linker to resolve.

Now, what happens with another compilation unit (array.cpp)? Compiler will parse the template definition and check for syntax correctness, but it will not generate the code for the member functions. How it could? In order to generate the code, compiler will need to know template parameters - it needs a type, not a template.

Therefore, linker will find the definition for array neither in main.cpp nor in array.cpp and therefore it will report an error for all unresolved member definitions.

OK. That answers the question 1. But what about question 2? We have four member functions defined in array.cpp, and only three error messages reported by linker. The answer is in the concept of lazy instantiation. In main.cpp we don't use operator[] and compiler never even tried to instantiate its definition.

Solutions
Now that we understand what the problem is, it would be nice to offer some solutions. Here they are:

<script src="fi_divexpand.js" type="text"></script>
1.Make the template definition visible to compiler in the point of instantiation.
2.Instantiate the types you need explicitly in a separate compile unit, so that linker can find it.
3.Use keyword export.


The first two are often called inclusion model, while the third is sometimes referred as separation model.

The first solution really means that we need to include not only template declarations, but also the definitions in every translation unit in which we use the templates. In our example it means that we will use the first version of array.h with all member functions inlined, or that we include array.cpp in our main.cpp. In that case, compiler will see both the declaration and definition of all member functions from array and it will be able to instantiate array . The drawback of this approach is that our compilation units can become huge, and it can increase build and link time significantly.

Now the second solution. We can explicitly instantiate the template for the types we need. It is best to keep all explicit instantiation directives in a separate compilation unit. In our example, we can add a new file templateinstantiations.cpp

<script src="fi_divexpand.js" type="text"></script>
// templateinstantiations.cpp? ? ? ? ? ? ? ?
#include "array.cpp"

template class array ; // explicit instantiation
? ? ? ?


Type array will be generated not in main.cpp but in templateinstantiations.cpp and linker will find its definition. With this approach, we don't have huge headers, and hence the build time will drop. Also, the header files will be "cleaner" and more readable. However, we don't have the benefits of lazy instantiation here (explicit instantiation generates the code for all member functions), and it can become tricky to maintain templateinstantiations.cpp for big projects.

The third solution is to mark the template definitions with the keyword export and the compiler will take care about the rest. When I read about export in the Stroustrup book, I was very enthusiastic about it. It took me several minutes to find out that it was not implemented on VC 6.0, and a little more to find out that no compiler supported this keyword at all (the first compiler that supports this keyword was released in late 2002). Since then, I have read more about export and learnt that it hardly solves any of the problems encountered with the inclusion model. For more information about issues with this keyword, I recommend articles by Herb Sutter.

Conclusion
In order to develop template libraries, we need to understand that template classes are not "ordinary types" and that we need to think differently when working with them. The purpose of this article was not to scare the developers who want to do some template programming. On the contrary, I hope it will help them to avoid some usual mistakes that people who start template development usually make.

Literature
Bjarne Stroustrup: "The C++ Programming Language", Addison-Wesley Pub Co; ISBN: 0201889544 ; 3rd edition (June 20, 1997)
David Vandevoorde, Nicolai M. Josuttis: "C++ Templates: The Complete Guide", Addison Wesley Professional; ISBN: 0201734842 ; 1st edition (November 12, 2002)


原文地址: http://www.codeproject.com/cpp/templatesourceorg.asp
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值