Solidity语法(八)其他

库(Libraries)

库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过EVM的特性DELEGATECALL(Homestead之前是用CALLCODE)来复用代码。这意味着库函数调用时,它的代码是在调用合约的上下文中执行。使用this将会指向到调用合约,而且可以访问调用合约的存储(storage)。因为一个合约是一个独立的代码块,它仅可以访问调用合约明确提供的状态变量(state variables),否则除此之外,没有任何方法去知道这些状态变量。

使用库合约的合约,可以将库合约视为隐式的父合约(base contracts),当然它们不会显式的出现在继承关系中。但调用库函数的方式非常类似,如库L有函数f(),使用L.f()即可访问。此外,internal的库函数对所有合约可见,如果把库想像成一个父合约就能说得通了。当然调用内部函数使用的是internal的调用惯例,这意味着所有internal类型可以传进去,memory类型则通过引用传递,而不是拷贝的方式。为了在EVM中实现这一点,internal的库函数的代码和从其中调用的所有函数将被拉取(pull into)到调用合约中,然后执行一个普通的JUMP来代替DELEGATECALL。

下面的例子展示了如何使用库(后续在using for章节有一个更适合的实现Set的例子)。

pragma solidity ^0.4.0;

library Set {
  // We define a new struct datatype that will be used to
  // hold its data in the calling contract.
  struct Data { mapping(uint => bool) flags; }

  // Note that the first parameter is of type "storage
  // reference" and thus only its storage address and not
  // its contents is passed as part of the call.  This is a
  // special feature of library functions.  It is idiomatic
  // to call the first parameter 'self', if the function can
  // be seen as a method of that object.
  function insert(Data storage self, uint value)
      returns (bool)
  {
      if (self.flags[value])
          return false; // already there
      self.flags[value] = true;
      return true;
  }

  function remove(Data storage self, uint value)
      returns (bool)
  {
      if (!self.flags[value])
          return false; // not there
      self.flags[value] = false;
      return true;
  }

  function contains(Data storage self, uint value)
      returns (bool)
  {
      return self.flags[value];
  }
}

contract C {
    Set.Data knownValues;

    function register(uint value) {
        // The library functions can be called without a
        // specific instance of the library, since the
        // "instance" will be the current contract.
        if (!Set.insert(knownValues, value))
            throw;
    }
    // In this contract, we can also directly access knownValues.flags, if we want.
}

上面的例子中:

  • Library定义了一个数据结构体,用来在调用的合约中使用(库本身并未实际存储的数据)。如果函数需要操作数据,这个数据一般是通过库函数的第一个参数传入,按惯例会把参数名定为self。
  • 另外一个需要留意的是上例中self的类型是storage,那么意味着传入的会是一个引用,而不是拷贝的值,那么修改它的值,会同步影响到其它地方,俗称引用传递,非值传递。
  • 库函数的使用不需要实例化,c.register中可以看出是直接使用Set.insert。但实际上当前的这个合约本身就是它的一个实例。
  • 这个例子中,c可以直接访问,knownValues。虽然这个值主要是被库函数使用的。
    当然,你完全可以不按上面的方式来使用库函数,可以不需要定义结构体,不需要使用storage类型的参数,还可以在任何位置有多个storage的引用类型的参数。

调用Set.contains,Set.remove,Set.insert都会编译为以DELEGATECALL的方式调用external的合约和库。如果使用库,需要注意的是一个实实在在的外部函数调用发生了。尽管msg.sender,msg.value,this还会保持它们在此调用中的值(在Homestead之前,由于实际使用的是CALLCODE,msg.sender,msg.value会变化)。

对比普通合约来说,库的限制:

  • 无状态变量(state variables)。
  • 不能继承或被继承
  • 不能接收ether。

这些限制将来也可能被解除!

附着库(Using for)

指令using A for B;用来附着库里定义的函数(从库A)到任意类型B。这些函数将会默认接收调用函数对象的实例作为第一个参数。语法类似,python中的self变量一样。

using A for *的效果是,库A中的函数被附着在做任意的类型上。

在这两种情形中,所有函数,即使那些第一个参数的类型与调用函数的对象类型不匹配的,也被附着上了。类型检查是在函数被真正调用时,函数重载检查也会执行。

using A for B;指令仅在当前的作用域有效,且暂时仅仅支持当前的合约这个作用域,后续也非常有可能解除这个限制,允许作用到全局范围。如果能作用到全局范围,通过引入一些模块(module),数据类型将能通过库函数扩展功能,而不需要每个地方都得写一遍类似的代码了。

下面我们来换个方式重写set的例子。

pragma solidity ^0.4.0;

// This is the same code as before, just without comments
library Set {
  struct Data { mapping(uint => bool) flags; }

  function insert(Data storage self, uint value)
      returns (bool)
  {
      if (self.flags[value])
        return false; // already there
      self.flags[value] = true;
      return true;
  }

  function remove(Data storage self, uint value)
      returns (bool)
  {
      if (!self.flags[value])
          return false; // not there
      self.flags[value] = false;
      return true;
  }

  function contains(Data storage self, uint value)
      returns (bool)
  {
      return self.flags[value];
  }
}

contract C {
    using Set for Set.Data; // this is the crucial change
    Set.Data knownValues;

    function register(uint value) {
        // Here, all variables of type Set.Data have
        // corresponding member functions.
        // The following function call is identical to
        // Set.insert(knownValues, value)
        if (!knownValues.insert(value))
            throw;
    }
}

你可能感兴趣的:(Solidity语法(八)其他)