http://www.kqlcn.com/

以太坊Solidity的库

  库与合约类似,它们只需要在特定的地址部署一次,并且它们的代码可以通过 EVM 的 DELEGATECALL (Homestead 之前使用 CALLCODE 关键字)特性进行重用。 这意味着如果库函数被调用,它的代码在调用合约的上下文中执行,即 this 指向调用合约,特别是可以访问调用合约的存储。 因为每个库都是一段独立的代码,所以它仅能访问调用合约明确提供的状态变量(否则它就无法通过名字访问这些变量)。 因为我们假定库是无状态的,所以如果它们不修改状态(也就是说,如果它们是 view 或者 pure 函数), 库函数仅可以通过直接调用来使用(即不使用 DELEGATECALL关键字), 特别是,除非能规避 Solidity 的类型系统,否则是不可能销毁任何库的。

  库可以看作是使用他们的合约的隐式的基类合约。虽然它们在继承关系中不会显式可见,但调用库函数与调用显式的基类合约十分类似 (如果 L 是库的话,可以使用 L.f() 调用库函数)。此外,就像库是基类合约一样,对所有使用库的合约,库的 internal 函数都是可见的。 当然,需要使用内部调用约定来调用内部函数,这意味着所有内部类型,内存类型都是通过引用而不是复制来传递。 为了在 EVM 中实现这些,内部库函数的代码和从其中调用的所有函数都在编译阶段被拉取到调用合约中,然后使用一个 JUMP 调用来代替 DELEGATECALL。

  下面的示例说明如何使用库(但也请务必看看 using for 有一个实现 set 更好的例子)。

pragma solidity ^0.4.16;

library Set {
 // 我们定义了一个新的结构体数据类型,用于在调用合约中保存数据。
 struct Data { mapping(uint => bool) flags; }

 // 注意第一个参数是“storage reference”类型,因此在调用中参数传递的只是它的存储地址而不是内容。
 // 这是库函数的一个特性。如果该函数可以被视为对象的方法,则习惯称第一个参数为 `self` 。
 function insert(Data storage self, uint value)
     public
     returns (bool)
 {
     if (self.flags[value])
         return false; // 已经存在
     self.flags[value] = true;
     return true;
 }

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

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

contract C {
   Set.Data knownValues;

   function register(uint value) public {
       // 不需要库的特定实例就可以调用库函数,
       // 因为当前合约就是“instance”。
       require(Set.insert(knownValues, value));
   }
   // 如果我们愿意,我们也可以在这个合约中直接访问 knownValues.flags。
}

  当然,你不必按照这种方式去使用库:它们也可以在不定义结构数据类型的情况下使用。 函数也不需要任何存储引用参数,库可以出现在任何位置并且可以有多个存储引用参数。

  调用 Set.contains,Set.insert 和 Set.remove 都被编译为外部调用( DELEGATECALL )。 如果使用库,请注意实际执行的是外部函数调用。 msg.sender, msg.value 和 this 在调用中将保留它们的值, (在 Homestead 之前,因为使用了 CALLCODE,改变了 msg.sender 和 msg.value)。

  以下示例展示了如何在库中使用内存类型和内部函数来实现自定义类型,而无需支付外部函数调用的开销:

pragma solidity ^0.4.16;

library BigInt {
   struct bigint {
       uint[] limbs;
   }

   function fromUint(uint x) internal pure returns (bigint r) {
       r.limbs = new uint[](1);
       r.limbs[0] = x;
   }

   function add(bigint _a, bigint _b) internal pure returns (bigint r) {
       r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));
       uint carry = 0;
       for (uint i = 0; i < r.limbs.length; i) {
           uint a = limb(_a, i);
           uint b = limb(_b, i);
           r.limbs[i] = a b carry;
           if (a b < a || (a b == uint(-1) && carry > 0))
               carry = 1;
           else
               carry = 0;
       }
       if (carry > 0) {
           // 太差了,我们需要增加一个 limb
           uint[] memory newLimbs = new uint[](r.limbs.length  1);
           for (i = 0; i < r.limbs.length; i)
               newLimbs[i] = r.limbs[i];
           newLimbs[i] = carry;
           r.limbs = newLimbs;
       }
   }

   function limb(bigint _a, uint _limb) internal pure returns (uint) {
       return _limb < _a.limbs.length _a.limbs[_limb] : 0;
   }

   function max(uint a, uint b) private pure returns (uint) {
       return a > b a : b;
   }
}

contract C {
   using BigInt for BigInt.bigint;

   function f() public pure {
       var x = BigInt.fromUint(7);
       var y = BigInt.fromUint(uint(-1));
       var z = x.add(y);
   }
}

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。