반응형

C++에서 map<key, value>를 쓰다 보면, value를 포인터로 관리해야 하는 경우가 있다. 예를 들어, 아래와 같은 형태의 코드이다.


#define <map>

using namespace std;


class Foo {

public:

    int a;

    double b;

    Foo(int a, double b){

        this->a = a;

        this->b = b;

    }

}


int main() {

    std::map<int, Foo*> fooMap;

    fooMap[101] = new Foo(10, 505.26);

    fooMap[104] = new Foo(8, 1233.61);

    // ...



    fooMap.erase(101);

    fooMap[105] = new Foo(17, 564.98);

    // ...


    fooMap.clear();

    return 0;

}


위와 같이 실행하면 메모리 누수가 발생한다. map.erase(key)를 이용해서 특정한 pair를 삭제하더라도 포인터를 통해 할당되어 있는 메모리 영역은 자동으로 해제(delete)되지 않는다. 즉, 명시적으로 코드 어디선가 new를 통해 오브젝트를 할당했으면, map의 value로 쓰이는지 여부에 관계 없이 명시적으로 delete를 해 주어야 한다.


실제로 C++의 erase와 clear 함수의 소스코드 위에 달려 있는 주석에도 같은 내용이 명시적으로 쓰여져 있다.


map erase 함수 주석:

  /**
   *  @brief Erases elements according to the provided key.
   *  @param  __x  Key of element to be erased.
   *  @return  The number of elements erased.
   *
   *  This function erases all the elements located by the given key from
   *  a %map.
   *  Note that this function only erases the element, and that if
   *  the element is itself a pointer, the pointed-to memory is not touched
   *  in any way.  Managing the pointer is the user's responsibility.
   */

...


map clear 함수 주석:

  /**
   *  Erases all elements in a %map.  Note that this function only
   *  erases the elements, and that if the elements themselves are
   *  pointers, the pointed-to memory is not touched in any way.
   *  Managing the pointer is the user's responsibility.
   */

...



포인터를 명시적으로 delete하는 것이 번거롭다면 C++11 이후의 버전들이 smart pointer를 제공하는데, (또는 이전 버전의 경우 boost::shared_ptr) 이것을 사용하면 자동으로 메모리 할당을 해제해 주므로 명시적인 delete를 할 필요가 없다.


포인터에 대해서 더이상 참조당하고 있지 않을 경우에 자동으로 메모리를 해제하는 작업은 ns-3에도 Ptr<T>로 구현되어 있는데, std::shared_ptr, boost::shared_ptr은 아직 써본 적이 없어서 자세히 모르겠지만 아마 개념과 목적은 유사할 것으로 예상된다.

(나중에 사용한 뒤에 포스트를 수정할 예정)




반응형
블로그 이미지

Bryan_

,
반응형

나는 여러 개의 특정한 데이터 여러 개를 묶어서 관리해야 할 때 습관적으로 vector를 많이 쓴다.


일단은 코딩하면서 생각 없이 vector<T>로 특정 타입의 데이터를 그룹화시키고, 점차 요구사항이 늘어남에 따라 해당 vector에 접근해서 특정 항목만추려내는 것 같은 새로운 기능을 함수로 만들어서 추가하곤 한다.


그렇게 코드를 확장해 가다가, 문득 특정 항목들을 모아 두는 vector에 값이 중복되어 들어가면 안 되는 요구사항을 발견했다. 원래부터 그런 요구사항이 있었는데, 미처 생각하지 못하고 일단 무작정 vector로 만들었다.

그래서 별 생각 없이 vector에 항목을 추가하는 함수에서 먼저 vector를 traverse하면서 새로 추가하려는 항목이 이미 존재하는지 확인하는 코드를 추가하려고 했다. loop를 만들다 보니, 갑자기 중복된 값을 허용하지 않는 데이터 구조인 set을 쓰면 굳이 이렇게 코딩할 필요가 없다는 것을 깨닫게 되었다. 즉, 데이터 구조로 set을 쓰고, 추가할 때 중복된 값의 존재 유무를 확인하는 코드를 추가할 필요도 없이 그냥 기존에 하던 대로 고민없이 추가하면 되는 것이었다.


내가 set 개념은 알고 있었지만, 평소에 잘 쓰지 않다 보니 이런 일이 생긴 것 같다. 고민 없이 너무 습관적으로 vector로 일단 만들고 보니 vector가 처리해 주지 못하는 이슈를 처리하기 위해서 나중에 코드를 추가로 보완해야 하는 상황이 된 것이다.


결국은 많이 써 보고, 조금만 더 데이터 구조의 적합성에 대한 고민을 해 본 다음에 코딩을 진행할 필요가 있다. 근본적으로 보자면, 요구사항을 도출하고 분석하는 단계에서 vector가 적합한지 미리 생각함으로써 설계 단계에서는 이미 적합한 데이터 구조가 결정되는 것이 이상적이다.


물론 내 경우에는 네트워크에서 라우팅 과정을 시뮬레이션으로 만들고, 그 과정에서 빈번하게 요구사항이 변화하기 때문에 어쩔 수는 없다. 하지만 적어도 어느 정도 선에서 완성해야 할 모양에 대한 그림이 나오면 그걸 완성하기 위한 요구사항 분석과 설계는 신경써서 함으로써, 나중에 안해도 될 코딩을 더 하거나 비효율적인 코드가 더해지는 상황은 예방하는 습관을 들여야 하겠다.




반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 14.04 (amd64)

python version: 2.7.6


파이썬에서 firebase에 접근하는 코드를 테스트하던 중에 아래와 같이 경고 메세지가 길게 출력이 되면서 어쨌든 실행은 정상적으로 되었다:

usera@usera-Linux:~/git/firebase_test$ python test2.py 

/usr/local/lib/python2.7/dist-packages/urllib3/util/ssl_.py:339: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

  SNIMissingWarning

/usr/local/lib/python2.7/dist-packages/urllib3/util/ssl_.py:137: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

  InsecurePlatformWarning

{요청했던 데이터}

usera@usera-Linux:~/git/firebase_test$ 


SSL 관련 라이브러리가 없어서 그런가 해서 pip install pyopenssl 설치를 시도해 보니 이미 설치되어 있었다.


인터넷을 뒤져 보니 사용하는 라이브러리의 버전이 낮아서 그럴 수도 있다는 댓글이 있어서 pyopenssl을 업그레이드 했더니 그 뒤로 경고 메세지가 뜨지 않고 문제가 해결되었다.


$ pip install pyopenssl --upgrade





반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 14.04 (amd64)

에러 발생한 버전: 5.5 (Ubuntu 14.04에서 apt-get으로 기본 설치되는 버전)



아마존웹서비스(AWS) 서버에 있는 MySQL 데이터베이스를 dump를 떠서 Ubuntu 14.04를 쓰는 개인 피씨에 옮기려고 했는데, 백업이 복구가 되지 않고 아래와 같은 에러가 발생하였다:


$ mysql -uroot -p test < test_170728.sql

Enter password: 

ERROR 1293 (HY000) at line 25: Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause



에러 메세지가 알려주는 것 그대로, 테이블 하나에 TIMESTAMP 타입의 column은 한개만 default로 CURRENT_TIMESTAMP를 지정할 수 있다는 내용이다.


참고로 이 제한사항은 5.6부터 해제되었기 때문에, 지금 쓰는 mysql-server-5.5를 mysql-server-5.6으로 업그레이드하면 문제가 해결된다.


$ sudo apt-get update

$ sudo apt-get install mysql-server-5.6


이미 5.5에서 관리하고 있던 데이터베이스가 있는 경우, 위와 같이 5.6을 설치하면 자동으로 데이터베이스가 이전되므로 별도로 dump를 떠서 옮길 필요는 없다. 다만 만약의 경우를 대비해서 5.6을 설치하기 전에 미리 백업을 해 두면 안전하긴 하다.





반응형
블로그 이미지

Bryan_

,
반응형

OS: Ubuntu 16.04 server (amd64)

Python: 3.5.2

Flask-restful: 0.3.5


Flask-restful을 이용하면 하나의 클래스가 REST API 1개를 처리할 수 있도록 만들 수 있다. 처음에는 Quickstart 페이지에 있는 HelloWorld 예제 [1]를 따라하면서 API 여러 개를 만들면서 클래스도 여러 개 만들고, 각 클래스에서 argument 처리 등 점점 복잡한 작업을 추가하기 시작하면서 소스 파일이 1개뿐이면서 길이가 아주 많이 길어지는 상황이 되었다.


하나의 소스 파일이 너무 길면 보기도 불편하고, 무엇보다 여러 사람이 REST API를 조금씩 나눠서 병렬적으로 코딩하는 경우에는 하나의 파일에 동시에 수정이 발생하므로 협업 측면에서도 관리하기 어렵다. 따라서 여러 개의 파일로 나눠서 관리하는 것이 좋다. 사실 어떻게 나눠야 할 지 모르겠어서 고민하다가, 그냥 파이썬이 클래스를 다른 소스 파일에서 import해 오는 구조를 그대로 쓰기만 하면 되는 간단한 원리임을 알 수 있었다.


예를 들면, 아래와 같은 HelloWorld 예제를 main.py로 두고,


<main.py>

from flask import Flask

from flask_restful import Resource, Api


app = Flask(__name__)

api = Api(app)


class HelloWorld(Resource):

    def get(self):

        return {'hello': 'world'}


api.add_resource(HelloWorld, '/')


if __name__ == '__main__':

    app.run(debug=True)



여기서 main.py는 서버를 실행하는 시작 포인트로만 쓰고 실제 REST API 처리하는 부분을 분리한다면, 아래와 같이 할 수 있다.


<hello.py>

from flask_restful import Resource


class HelloWorld(Resource):

    def get(self):

        return {'hello': 'world'}



<main.py>

from flask import Flask

from flask_restful import Resource, Api

from hello import HelloWorld


app = Flask(__name__)

api = Api(app)


api.add_resource(HelloWorld, '/')


if __name__ == '__main__':

    app.run(debug=True)


이제 main.py 파일에서는 클래스를 import하고 API 정의만 나열해 주고, 실제 API 처리 파일은 hello.py에서 수정하는 구조가 되었다.



<참고자료>

[1] Flask-RESTful Quickstart, http://flask-restful.readthedocs.io/en/0.3.5/quickstart.html



반응형
블로그 이미지

Bryan_

,