最近在编译ZeroMQ
源码时, 出现了一个编译错误,其本质是涉及到C++的一个编译规范.编译出错的源码文件为:
/*
Copyright (c) 2007 FastMQ Inc.
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_ENCODER_HPP_INCLUDED__
#define __ZMQ_ENCODER_HPP_INCLUDED__
#include <assert.h>
#include <algorithm>
#include "dispatcher_proxy.hpp"
#include "cmsg.hpp"
namespace zmq
{
template <typename T> class encoder_t
{
public:
encoder_t (dispatcher_proxy_t *proxy_, int source_thread_id_,
size_t chunk_size_) :
proxy (proxy_),
source_thread_id (source_thread_id_),
chunk_size (chunk_size_)
{
init_cmsg (msg);
}
~encoder_t ()
{
free_cmsg (msg);
}
// The chunk after being used should be deallocated
// using standard 'free' function
bool read (unsigned char **data_, size_t *size_)
{
unsigned char *chunk = (unsigned char*) malloc (chunk_size);
assert (chunk);
size_t pos = 0;
while (true) {
if (to_write) {
size_t to_copy = std::min (to_write, chunk_size - pos);
memcpy (chunk + pos, write_pos, to_copy);
pos += to_copy;
write_pos += to_copy;
to_write -= to_copy;
}
else {
if (!((static_cast <T*> (this)->*next) ()))
{
*data_ = chunk;
*size_ = pos;
return false;
}
continue;
}
if (pos == chunk_size)
break;
}
*data_ = chunk;
*size_ = pos;
return true;
}
protected:
typedef bool (T::*parse_step_t) ();
inline void next_step (void *write_pos_, size_t to_write_,
parse_step_t next_)
{
write_pos = (unsigned char*) write_pos_;
to_write = to_write_;
next = next_;
}
inline bool fetch ()
{
// TODO: review the deallocation of msg (w.r.t. zero copy)
free_cmsg (msg);
init_cmsg (msg);
return proxy->read (source_thread_id, &msg);
}
cmsg_t msg;
private:
dispatcher_proxy_t *proxy;
int source_thread_id;
size_t chunk_size;
unsigned char *write_pos;
size_t to_write;
parse_step_t next;
};
}
#endif
使用make
编译源码工程时,出现如下错误:
encoder.hpp:61:60: error: there are no arguments to 'memcpy' that depend on a template parameter, so a declaration of 'memcpy' must be available [-fpermissive]
memcpy (chunk + pos, write_pos, to_copy);
^
encoder.hpp:61:60: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
C++标准要求,对于依赖于模板参数的名字,将在模板类(或函数)实例化的时候进行名字查找,对于不依赖模板参数的名字,将搜索当前的定义. 举例说明, 如下类定义:
template <typename T>
class C {
T t;
int get() {
return t.getValue();
}
};
成员函数get依赖于模板参数T,因此对于get的实现是否正确,只有在模板类C实例化并调用get函数的时候才知道.如果模板类C没有实例化,这个类定义肯定是编译正确的,因为编译器无从判断模板参数T是否具有成员函数getValue.这个情况是典型的场景. 更需要注意的是具有继承关系的场景,举例如下:
template <typename T> class B {
int f(){return 0;};
};
template <typename T> class D : public B<T> {
int get() { return f(); }
};
这里子类D的get方法调用父类中的方法f,而f是不依赖其模板参数的名字,编译器将搜索f定义,而不是对应父类中的名字.所以编译器会产生类似前文一样的错误.为了更清晰的表明子类使用父类中的成员函数,需要使用模板参数依赖的方法来访问父类的成员函数.有2种形式可以消除这个错误,使用
int get() { return B<T>::f(); }
或者
int get() { return this->f(); }
当然,正如编译器错误信息提示,可以使用-fpermissive
标志让编译器接受这种代码格式,但是这不是推荐的行为.
回到开始ZeroMQ的编译错误,函数memcpy
没有依赖类encoder_t
的模板参数,但是不同的是,这里不能用依赖模板参数的形式访问memcpy,因为memcpy是个库函数,而不是模板类的成员函数,这里的问题本质是没要找到memcpy的定义,缺少了相应的头文件string.h
, 因此包含该头文件即可.