Solidity 구문 (8) 기타

5080 단어

라이브러리(Libraries)


라이브러리는 계약과 유사하지만, 지정한 주소에 한 번만 배치하고, EVM의 특성인 DELEGATECALL (Homestead 이전에는 CALLCODE) 을 통해 코드를 복원하는 것이 목적이다.이것은 라이브러리 함수를 호출할 때, 그 코드는 호출 계약의 상하문에서 실행된다는 것을 의미한다.this를 사용하면 호출 계약을 가리키며 호출 계약의 저장소(storage)에 접근할 수 있습니다.하나의 계약은 하나의 독립된 코드 블록이기 때문에, 계약이 명확하게 제공한 상태 변수 (state variables) 를 호출하는 데만 접근할 수 있고, 그렇지 않으면 이 상태 변수를 알 수 있는 방법이 없다.
라이브러리 계약을 사용하는 계약은 라이브러리 계약을 은밀한 부모 계약(base contracts)으로 볼 수 있으며, 물론 상속 관계에 현저하게 나타나지 않는다.그러나 라이브러리 함수를 호출하는 방식은 매우 유사하다. 예를 들어 라이브러리 L에 함수 f()가 있으면 L.f()를 사용하면 접근할 수 있다.그 밖에 인터넷의 라이브러리 함수는 모든 계약에 대해 알 수 있다. 라이브러리를 하나의 부계약으로 상상하면 통할 수 있다.물론 내부 함수를 호출하는 것은 인터넷의 호출 관례를 사용하는데 이것은 모든 인터넷 유형이 들어갈 수 있음을 의미하고memory 유형은 복사하는 방식이 아니라 인용을 통해 전달된다는 것을 의미한다.EVM에서 이를 실현하기 위해 인터넷의 라이브러리 함수 코드와 그 중에서 호출된 모든 함수는 호출 계약에 끌어다 놓은 다음 DELEGATECALL 대신 일반적인 JUMP를 실행합니다.
다음의 예는 라이브러리를 어떻게 사용하는지 보여 준다. (다음에 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;
        }
    }
    

    좋은 웹페이지 즐겨찾기