Status of the C++11 Migrator

Monday, April 15, 2013

Status of the C++11 Migrator

Since the design document for cpp11-migrate, the C++11 migrator tool, was first proposed in early December 2012 development has been making steady progress. In this article I’ll talk about what’s been implemented in cpp11-migrate so far, what’s coming up, and how you can get involved.

The purpose of the C++11 Migrator is to do source-to-source translation to migrate existing C++ code to use C++11 features to enhance maintainability, readability, runtime performance, and compile-time performance. Development is still early and transforms fall mostly into the first two categories. The migrator is based on Clang’s LibTooling and the AST Matching library.

Most of the development so far has been carried out by a small core group at Intel. Our focus so far has been to set up project infrastructure and testing, implement a few basic transforms, and make sure those transforms work well. Our aim is to make this tool useful to the community so we’re always listening for transform ideas and feedback.

How to Get cpp11-migrate

cpp11-migrate is located in the Extra Clang Tools repository. To build cpp11-migrate, you will need the LLVM and Clang sources as well. Follow the directions in Clang’s Getting Started instructionscpp11-migrate.check-clang-tools

The Transforms So Far

The C++11 Migrator currently supports four features of C++11:

  • Range-based for loops
  • The nullptr literal for null pointers
  • The auto type specifier
  • override virtual specifier

The range-based for-loop transform once existed as a stand-alone tool called contributed by Sam Panzer. When development on more transforms started, the idea became to pull all transforms under the jurisdiction of a single tool and was born. The range-based for-loop transform replaces for-loops used in one of the following three common situations:

The range-based for-loop transform once existed as a stand-alone tool called contributed by Sam Panzer. When development on more transforms started, the idea became to pull all transforms under the jurisdiction of a single tool and was born. The range-based for-loop transform replaces for-loops used in one of the following three common situations:

  1. Loops over containers using iterators

  2. std::vector<int> myVec;
    for (std::vector<int>::iterator I = myVec.begin(),
                                    E = myVec.end();
         I != E; ++I)
      llvm::outs() << *I;
    
  3. std::vector<int> myVec;
    for (auto & elem : myVec)
      llvm::outs() << elem;
    
  4. Loops over statically allocated arrays

  5. int arr[] = {1,2,3,4,5};
    for (int i = 0; i < 5; ++i)
      llvm::outs() << arr[i];
    
  6. int arr[] = {1,2,3,4,5};
    for (auto & elem : arr)
      llvm::outs() << elem;
    
  7. Loops over array-like containers using operator[] or at().

  8. std::vector<int> myVec;
    for (int i = 0; i < myVec.size(); ++i)
      llvm::outs() << v[i];
    
  9. std::vector<int> myVec;
    for (auto & elem : myVec)
      llvm::outs() << elem;
    

The nullptr transform uses the new literal where pointers are being initialized with or assigned a null value. In cases where an explicit cast is used, the explicit cast is left behind to avoid introducing ambiguities into the code.

void foo(int *arg);
void foo(float *arg);

int *IntPtr = 0;
float *FloatPtr = NULL;
foo(static_cast<int*>(0));

void foo(int *arg);
void foo(float *arg);

int *IntPtr = nullptr;
float *FloatPtr = nullptr;
foo(static_cast<int*>(nullptr));

The auto type specifier transform replaces the type specifier for variable declarations with the new keyword. In general, such a replacement can be done whenever the type of the variable declaration matches the type of its initializer. However, the transform targets only a few specific useful situations with readability and maintainability in mind:

  1. When the variable is an iterator for an STL container.

  2. std::vector<std::pair<int, std::string> >::iterator NameAgeI = People.begin();
    for (std::vector<MyType>::iterator I = Container.begin(),
                                       E = Container.end;
         I != E; ++I) {
      // ...
    }
    
  3. auto NameAgeI = People.begin();
    for (auto I = Container.begin(),
                                      E = Container.end;
         I != E; ++I) {
      // ...
    }
    
  4. When the initializer is an allocation using the new operator.

  5. MyType *VarPtr = new MyType();
    MyType * const VarCPtr = new MyType();
    
  6. auto VarPtr = new MyType();
    auto const VarCPtr = new MyType();
    

Support for a third situation is in development: creating objects with factory functions.

MyType *FooPtr = makeObject<MyType>(/*...*/);
MyType *BarPtr = MyType::create(/*...*/);

auto FooPtr = makeObject<MyType>(/*...*/);
auto BarPtr = MyType::create(/*...*/);

In each situation, the deduced type for the declared variable should be obvious to the reader. Iterators for standard containers are created by functions with specific names and are used in specific situations. For factory functions and operator newthe type is spelled out in the initializer so repeating it in the variable declaration is not necessary.

The override virtual specifier transform, contributed by Philip Dunstan, is the migrator’s fourth transform and the first to be contributed from outside the core group at Intel. This transform detects virtual member functions in derived classes that override member functions from parent classes and adds the override virtual specifier to the function.

class Parent {
public:
  virtual int getNumChildren();
};

class Child {
public:
  virtual int getNumChildren();
};

class Parent {
public:
  virtual int getNumChildren();
};

class Child {
public:
  virtual int getNumChildren() override;
};

More details on these transforms, what they can and can’t do, how to adjust their behaviour, and known limitations can be found in the .

Testing on Real Projects

What better way to test the C++11 Migrator than to run it on entire real projects? We’ve set up a continuous integration server to build and run cpp11-migrate on two projects so far and have plans for at least three more. For each project, the goal is to build the transformed code and run that project’s test suite to ensure semantics haven’t changed.

Implemented:

  1. LLVM 3.1
  2. ITK 4.3.1

Planned:

  1. LLDB
  2. OpenCV
  3. Poco

Running the migrator on real code has been enormously helpful for finding bugs. Real code from varying projects often reveals code expressions not accounted for in the development and unit testing of the transforms. Every time a bug found from transforming these projects gets fixed, new test cases are added to the regression test suite and the migrator becomes more robust.

Future Work

Fixing bugs found by migrating real code is of high priority right now since we want a good user experience for as many people as we can as soon as possible. Adding more transforms is another priority and those transforms with the most interest from the community will come first. Currently at the top of the list are:

  1. Use the standard library instead of TR1
  2. Replace use of the deprecated auto_ptr class.

In addition to fixing bugs and adding transforms, there are also more general improvements to consider. One such improvement we’re making progress on is to remove the restriction that only source files are transformed and not any of the headers they include. The restriction has been in place until now because the migrator needs to know which headers are safe to transform. System headers and third-party library headers clearly shouldn’t be touched.

Get Involved!

If you want to get involved, the first thing you can do is try out cpp11-migrate on your code. Bugs can be logged with LLVM’s bug tracker under the product clang-tools-extra. Send an email to the Clang Developer’s Mailing List if you need help or would like to get more involved. We look forward to hearing from you!

Posted by Edwin Vane at 5:53 AM

Labels: C++, Clang

Location: Waterloo, ON, Canada

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值