<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 庫(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 =&gt; 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`會變化)。 下面的例子演示了如何使用`memory`類型和`內部函數(inernal function)`,來實現一個自定義類型,但不會用到`外部函數調用(external function)`。 ``` pragma solidity ^0.4.0; library BigInt { struct bigint { uint[] limbs; } function fromUint(uint x) internal returns (bigint r) { r.limbs = new uint[](1); r.limbs[0] = x; } function add(bigint _a, bigint _b) internal returns (bigint r) { r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); uint carry = 0; for (uint i = 0; i &lt; r.limbs.length; ++i) { uint a = limb(_a, i); uint b = limb(_b, i); r.limbs[i] = a + b + carry; if (a + b &lt; a || (a + b == uint(-1) &amp;&amp; carry &gt; 0)) carry = 1; else carry = 0; } if (carry &gt; 0) { // too bad, we have to add a limb uint[] memory newLimbs = new uint[](r.limbs.length + 1); for (i = 0; i &lt; r.limbs.length; ++i) newLimbs[i] = r.limbs[i]; newLimbs[i] = carry; r.limbs = newLimbs; } } function limb(bigint _a, uint _limb) internal returns (uint) { return _limb &lt; _a.limbs.length ? _a.limbs[_limb] : 0; } function max(uint a, uint b) private returns (uint) { return a &gt; b ? a : b; } } contract C { using BigInt for BigInt.bigint; function f() { var x = BigInt.fromUint(7); var y = BigInt.fromUint(uint(-1)); var z = x.add(y); } } ``` 因為編譯器并不知道庫最終部署的地址。這些地址須由`linker`填進最終的字節碼中(使用[命令行編譯器](http://solidity.readthedocs.io/en/develop/miscellaneous.html#commandline-compiler)來進行聯接)。如果地址沒有以參數的方式正確給到編譯器,編譯后的字節碼將會仍包含一個這樣格式的占們符`_Set___`(其中`Set`是庫的名稱)。可以通過手動將所有的40個符號替換為庫的十六進制地址。 對比普通合約來說,庫的限制: - 無`狀態變量(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 =&gt; 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; } } ``` 我們也可以通過這種方式來擴展`基本類型(elementary types)`。 ``` pragma solidity ^0.4.0; library Search { function indexOf(uint[] storage self, uint value) returns (uint) { for (uint i = 0; i &lt; self.length; i++) if (self[i] == value) return i; return uint(-1); } } contract C { using Search for uint[]; uint[] data; function append(uint value) { data.push(value); } function replace(uint _old, uint _new) { // This performs the library function call uint index = data.indexOf(_old); if (index == uint(-1)) data.push(_new); else data[index] = _new; } } ``` 需要注意的是所有庫調用都實際上是EVM函數調用。這意味著,如果你傳的是`memory`類型的,或者是`值類型(vaue types)`,那么僅會傳一份拷貝,即使是`self`變量。變通之法就是使用`存儲(storage)`類型的變量,這樣就不會拷貝內容。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看