Leveldb 编译错误背后的C++标准变化

标签: 编程 C Language Class Friendship Standard | 发表时间:2011-09-23 15:42 | 作者:Solrex Yang ndv
出处:http://blog.solrex.org

在编译 Levedb 时,我遇到了这个错误:

g++ -c -I. -I./include -fno-builtin-memcmp -DLEVELDB_PLATFORM_POSIX -pthread -DOS_LINUX -O2 -DNDEBUG db/version_set.cc -o db/version_set.o
db/version_set.cc: In member function `void leveldb::VersionSet::Builder::Apply(leveldb::VersionEdit*)':
./db/version_edit.h:100: error: `std::vector, std::allocator > > leveldb::VersionEdit::compact_pointers_' is private
db/version_set.cc:461: error: within this context
...

在网上容易搜到解决方案,由于归根结底是访问控制问题,方法是把所有涉及到的的 private 变量或类型修改为 public。由于不是所有的编译器都会报错,我就很好奇产生这个错误的根本原因。

BTW: 一种不修改代码的 work around 方法是,在编译这个文件时加上 -fno-access-control 参数,这样 g++ 就不会进行访问控制检查,自然也就没问题了。这个参数同样可以用于对 private 成员函数进行单元测试。

简单地分析一下这个错误。发生错误的地方是在 VersionSet::Builder 这个类的成员函数中,而错误则是其成员函数无法访问 VersionEdit 和 Version 类的私有成员变量。VersionSet 是 VersionEdit 和 Version 类的友元类,Builder 是 VersionSet 的嵌套类。简化一下,代码如下所示:

class VersionSet;

class VersionEdit {
    friend class VersionSet;
    static int compact_pointers_;
};

class VersionSet {
    class Builder {
        int foo()
        {  
            return VersionEdit::compact_pointers_;
        }  
    }; 
};

把这段代码拿给编译器去编译,g++ 3.4.4/5 会报类似的 `int VersionEdit::compact_pointers_' is private 错误,但是 g++ 4.5.3 则能够编译通过。

由于 VersionSet 是 VersionEdit 的友元类,那么 VersionSet 是能够访问 VersionEdit 私有成员的,这样问题就集中在 Builder 是否能够获得与 VersionEdit 的友元关系。如果语法规定嵌套类 Builder 能够从 VersionSet “获得”友元关系,那么 Builder就能够访问 VersionEdit::compact_pointers_,反之就不能访问。

在 C++98 标准中,关于嵌套类的权限有如下描述:

$11.8/1 [class.access.nest],

The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (clause 11) shall be obeyed.

Example:

class E {
    int x;
    class B { };
    class I {
        B b;                 // error: E::B is private
        int y;
        void f(E* p, int i) {
           p->x = i;         // error: E::x is private
        }
   };
   int g(I* p)
   {
       return p->y;          // error: I::y is private
   }
};

但是在 C++11 中,这段描述变更为:

$11.7/1 Nested classes [class.access.nest]

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11) shall be obeyed.

Example:

class E {
    int x;
    class B { };
    class I {
        B b;                  // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i;         // OK: E::I can access E::x
        }  
    }; 
    int g(I* p) {
        return p->y;          // error: I::y is private
    }  
};

从上面的描述和示例代码对比中我们可以明显看出,在旧标准中嵌套类和“被嵌套类”没有什么特殊的关系,就像两个普通类一样;但是在新标准中嵌套类已经完全视为“被嵌套类”的成员,那么自然也获得了“被嵌套类”成员应该有的访问控制权限。这也就意味着“被嵌套类”的普通成员拥有的访问“被友元类”私有成员变量的权限,嵌套类也能够获得,那么 Leveldb 在新版本的编译器下能够编译通过也不足为奇了。

不过 gcc3.4 的编译错误问题还不能单单归究于标准的变化。因为 gcc3.4 已经能够支持嵌套类访问“被嵌套类”的私有成员(因为在很早以前这就被确认为一个缺陷),只是不能够支持友元关系到嵌套类的传递。友元关系的传递可能是在 4.1 或者 4.2 版本中实现的,应该属于上述标准变化的衍生特性。

您可能对这些感兴趣:

相关 [leveldb 编译 错误] 推荐:

Leveldb 编译错误背后的C++标准变化

- ndv - Solrex Shuffling
在编译 Levedb 时,我遇到了这个错误:. 在网上容易搜到解决方案,由于归根结底是访问控制问题,方法是把所有涉及到的的 private 变量或类型修改为 public. 由于不是所有的编译器都会报错,我就很好奇产生这个错误的根本原因. BTW: 一种不修改代码的 work around 方法是,在编译这个文件时加上 -fno-access-control 参数,这样 g++ 就不会进行访问控制检查,自然也就没问题了.

Google开源LevelDB

- 酿泉 - Solidot
Google宣布在BSD许可证下开源其键值存储引擎LevelDB. LevelDB C++库可用于多种不同环境,如被浏览器用于存储最近访问的网页缓存,或者被操作系统使用去储存安装的软件包和依赖包清单,或被应用程序用于存储用户设置. Google称,即将发布的新版Chrome浏览器,就包含了基于LevelDB的IndexedDB HTML5 API实现.

LevelDB:实现(译)

- 高春辉 - 银河里的星星
  作者:Jeff Dean, Sanjay Ghemawat. 原文:http://leveldb.googlecode.com/svn/trunk/doc/impl.html. 译者:phylips@bmy 2011-8-17. 出处:http://duanple.blog.163.com/blog/static/7097176720112643946178/ .

LevelDB内部实现

- Ben - NoSQLFan
本文是一篇转载的翻译文章,翻译对象是LevelDB的官方文档中实现一章,主要描述了LevelDB内部的数据结构,文件结构及相关的存储,压缩恢复等功能的实现过程,看完后你就能知道,LevelDB为什么会叫这个名字了. 作者:Jeff Dean, Sanjay Ghemawat. 原文:leveldb.googlecode.com.

cpy-leveldb:Python 版的 LevelDB

- Ken - python.cn(jobs, news)
>>> db.Get("2") '222' >>> db.Get("5") '' >>> db.Write(batch) >>> db.Get("5") 'hello world 5' >>> db.Get("2") 'hello world 2' >>> iter = leveldb.Iterator(db) Iterator_init executed.

LevelDB学习交流

- gnawux - NoSQLFan
下面PPT作者是@淘宝解伦,PPT中对LevelDB的特点、设计思想及实现原理都有所涵盖,是一篇不错的LevelDB入门教材. 对LevelDB感兴趣的同学可以看看. LevelDB中的Skip List(跳跃表). 一个NoSQL与MongoDB的介绍PPT. RethinkDB 与 TokuDB 调研测试报告.

LevelDB实现解析

- - NoSQLFan
LevelDB是Google开发的一个key-value存储,其已经作为存储引擎被Riak和Kyoto Tycoon所支持( 这里和 这里),在国内 淘宝的 Tair开源key-value存储也已经将LevelDB作为其持久化存储引擎,并部署在线上使用. 下面PDF就是 淘宝核心系统研发团队的那岩总结的LevelDB内部实现的长文.

Leveldb的实现原理

- - 互联网旁观者
LevelDb日知录之一:LevelDb 101. 说起LevelDb也许您不清楚,但是如果作为IT工程师,不知道下面两位大神级别的工程师,那您的领导估计会Hold不住了:Jeff Dean和Sanjay Ghemawat. 这两位是Google公司重量级的工程师,为数甚少的Google Fellow之二.

LevelDB 的原理和动机

- - idea's blog
为了持久化, 必须写硬盘.. 为了快速写入硬盘, 必须采用追加方式顺序写到 log 文件. 这导致 log 文件中的数据是无序的.. 为了快速从硬盘中读取数据, 基于查找算法和局部性原理考虑, 必须将数据排序组织到 sst 文件中.. 多个 sst 文件而不是单个. 为了快速的插入数据到 sst 文件中, 必须使用多个 sst 文件, 每个 sst 文件只保存一定范围的数据.