pegacornの日記 このページをアンテナに追加 RSSフィード

2010-09-25 [C++][Singleton]

[][]DLLでLoki::SingletonHolderを使う場合の注意点

C++Singletonクラスを作成するのに便利なのがLoki::SingletonHolderクラス。しかし、Loki::SingletonHolderはテンプレートクラスかつ静的メンバ変数オブジェクトを管理しているため、ダイナミックリンクライブラリDLL)で使う場合には注意が必要だ。

◇直接Loki::SingletonHolderクラスを使った場合

package1/Foo.h
#ifndef PACKAGE1_FOO_H
#define PACKAGE1_FOO_H

#include "package1/package1.h"
#include <loki/Singleton.h>

namespace package1
{

class PACKAGE1_API Foo
{
    friend Loki::CreateUsingNew<Foo>;
    friend Loki::DefaultLifetime<Foo>;

private:
    Foo();
    Foo(const Foo &);
    Foo &operator=(const Foo &);
    ~Foo();

public:
    void func();
};

typedef Loki::SingletonHolder<Foo> FooHolder;

}

#endif
package1/Foo.cpp
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

Foo::Foo()
{
    std::cout << "Foo::Foo() called, this: " << this << '\n';
}

Foo::~Foo()
{
    std::cout << "Foo::~Foo() called, this: " << this << '\n';
}

void Foo::func()
{
    std::cout << "Foo::func() called, this: " << this << '\n';
}

}
package1/use_foo.cpp
#include "package1/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

void use_foo()
{
    std::cout << "-> package1::use_foo()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    std::cout << "<- package1::use_foo()\n";
}

}
package2/use_foo.cpp
#include "package2/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

using namespace package1;

namespace package2
{

void use_foo()
{
    std::cout << "-> package2::use_foo()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    std::cout << "<- package2::use_foo()\n";
}

}
main/main.cpp
#include "package1/Foo.h"
#include "package1/use_foo.h"
#include "package2/use_foo.h"
#include <tchar.h>
#include <iostream>

using namespace package1;

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "-> main()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    package1::use_foo();
    package2::use_foo();

    std::cout << "<- main()\n";
    return 0;
}
main.exeの実行結果
-> main()
Foo::Foo() called, this: 003D80B8
Foo::func() called, this: 003D80B8
-> package1::use_foo()
Foo::Foo() called, this: 003D8160
Foo::func() called, this: 003D8160
<- package1::use_foo()
-> package2::use_foo()
Foo::Foo() called, this: 003D8190
Foo::func() called, this: 003D8190
<- package2::use_foo()
<- main()
Foo::~Foo() called, this: 003D80B8
Foo::~Foo() called, this: 003D8190
Foo::~Foo() called, this: 003D8160

パッケージ毎にFooオブジェクトが生成されてしまっていることが確認できる。

これを防ぐには、Loki::SingletonHolderを直接使うのではなく、Loki::Singletonクラスを経由して使う。

◇Loki::Singletonクラスを経由してLoki::SingletonHolderを使った場合

package1/Foo.h
#ifndef PACKAGE1_FOO_H
#define PACKAGE1_FOO_H

#include "package1/package1.h"
#define LOKI_SINGLETON_EXPORT PACKAGE1_API
#include <loki/Singleton.h>
#undef LOKI_SINGLETON_EXPORT

namespace package1
{

class PACKAGE1_API Foo
{
    friend Loki::CreateUsingNew<Foo>;
    friend Loki::DefaultLifetime<Foo>;

private:
    Foo();
    Foo(const Foo &);
    Foo &operator=(const Foo &);
    ~Foo();

public:
    void func();
};

}

#endif
package1/Foo.cpp
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

Foo::Foo()
{
    std::cout << "Foo::Foo() called, this: " << this << '\n';
}

Foo::~Foo()
{
    std::cout << "Foo::~Foo() called, this: " << this << '\n';
}

void Foo::func()
{
    std::cout << "Foo::func() called, this: " << this << '\n';
}

typedef Loki::SingletonHolder<Foo> FooHolder;

}

LOKI_SINGLETON_INSTANCE_DEFINITION(package1::FooHolder);
template class Loki::Singleton<package1::FooHolder>;
package1/use_foo.cpp
#include "package1/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

void use_foo()
{
    std::cout << "-> package1::use_foo()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    std::cout << "<- package1::use_foo()\n";
}

}
package2/use_foo.cpp
#include "package2/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

using namespace package1;

namespace package2
{

void use_foo()
{
    std::cout << "-> package2::use_foo()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    std::cout << "<- package2::use_foo()\n";
}

}
main/main.cpp
#include "package1/Foo.h"
#include "package1/use_foo.h"
#include "package2/use_foo.h"
#include <tchar.h>
#include <iostream>

using namespace package1;

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "-> main()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    package1::use_foo();
    package2::use_foo();

    std::cout << "<- main()\n";
    return 0;
}
main.exeの実行結果
-> main()
Foo::Foo() called, this: 003D80B8
Foo::func() called, this: 003D80B8
-> package1::use_foo()
Foo::func() called, this: 003D80B8
<- package1::use_foo()
-> package2::use_foo()
Foo::func() called, this: 003D80B8
<- package2::use_foo()
<- main()
Foo::~Foo() called, this: 003D80B8

こうすることで、Fooオブジェクトが一つだけ生成される。

2007-04-18 [Ruby][clone]

[][]深いコピーの作成(4)

深いコピーの作成(3) - pegacornの日記

同一オブジェクトのコピーが複数作成される場合がある障害を修正した。

clone.rb
#!ruby

class Object
  def deep_clone
    _deep_clone({})
  end

  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = clone
    cloning_map[self] = cloning_obj
    cloning_obj.instance_variables.each do |var|
      val = cloning_obj.instance_variable_get(var)
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        next
      end
      cloning_obj.instance_variable_set(var, val)
    end
    cloning_obj
  end
end

class Array
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    cloning_obj.map! do |val|
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        #
      end
      val
    end
    cloning_obj
  end
end

class Hash
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    pairs = cloning_obj.to_a
    cloning_obj.clear
    pairs.each do |pair|
      pair.map! do |val|
        begin
          val = val._deep_clone(cloning_map)
        rescue TypeError
          #
        end
        val
      end
      cloning_obj[pair[0]] = pair[1]
    end
    cloning_obj
  end
end

2007-04-17 [Ruby][clone]

[][]深いコピーの作成(3)

深いコピーの作成(2) - pegacornの日記

作業用のインスタンス変数が衝突する可能性があった問題を解決した。

clone.rb
#!ruby

class Object
  def deep_clone
    _deep_clone({})
  end

  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = clone
    cloning_map[self] = cloning_obj
    cloning_obj.instance_variables.each do |var|
      val = cloning_obj.instance_variable_get(var)
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        next
      end
      cloning_obj.instance_variable_set(var, val)
    end
    cloning_map.delete(self)
  end
end

class Array
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    cloning_map[self] = cloning_obj
    cloning_obj.map! do |val|
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        #
      end
      val
    end
    cloning_map.delete(self)
  end
end

class Hash
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    cloning_map[self] = cloning_obj
    pairs = cloning_obj.to_a
    cloning_obj.clear
    pairs.each do |pair|
      pair.map! do |val|
        begin
          val = val._deep_clone(cloning_map)
        rescue TypeError
          #
        end
        val
      end
      cloning_obj[pair[0]] = pair[1]
    end
    cloning_map.delete(self)
  end
end

2007-04-12 [Ruby][clone]

[][]深いコピーの作成(2)

深いコピーの作成(1)

組み込みクラス Array, Hash 用のメソッドを作ってみた。

clone.rb
#!ruby

class Object
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = clone
    @deep_cloning_obj.instance_variables.each do |var|
      val = @deep_cloning_obj.instance_variable_get(var)
      begin
        @deep_cloning = true
        val = val.deep_clone
      rescue TypeError
        next
      ensure
        @deep_cloning = false
      end
      @deep_cloning_obj.instance_variable_set(var, val)
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end

class Array
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = super
    @deep_cloning_obj.map! do |val|
      begin
        @deep_cloning = true
        val = val.deep_clone
      rescue TypeError
        #
      ensure
        @deep_cloning = false
      end
      val
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end

class Hash
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = super
    pairs = @deep_cloning_obj.to_a
    @deep_cloning_obj.clear
    pairs.each do |pair|
      pair.map! do |val|
        begin
          @deep_cloning = true
          val = val.deep_clone
        rescue TypeError
          #
        ensure
          @deep_cloning = false
        end
        val
      end
      @deep_cloning_obj[pair[0]] = pair[1]
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end
実行例1
require 'clone'

class Foo
end

class Bar
end

obj = [Foo.new, Bar.new]
p obj                   # => [#<Foo:0x100ed494>, #<Bar:0x100ed480>]
p obj.clone             # => [#<Foo:0x100ed494>, #<Bar:0x100ed480>]
p obj.deep_clone        # => [#<Foo:0x100ed340>, #<Bar:0x100ed318>]
実行例2
require 'clone'

class Foo
end

class Bar
end

obj = {Foo.new => Bar.new, Foo.new => Bar.new}
p obj                   # => {#<Foo:0x100ed3a4>=>#<Bar:0x100ed390>, #<Foo:0x100ed37c>=>#<Bar:0x100ed368>}
p obj.clone             # => {#<Foo:0x100ed3a4>=>#<Bar:0x100ed390>, #<Foo:0x100ed37c>=>#<Bar:0x100ed368>}
p obj.deep_clone        # => {#<Foo:0x100ed188>=>#<Bar:0x100ed160>, #<Foo:0x100ed138>=>#<Bar:0x100ed110>}

[][]深いコピーの作成(1)

Ruby リファレンスマニュアル - Object#clone

Ruby には、オブジェクトの深い(deep)コピーを作成するメソッドが無い*1ので、作ってみた。

ただし、情報をインスタンス変数に保持していない組み込みクラスには、未だ対応していない。

clone.rb
#!ruby

class Object
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = clone
    @deep_cloning_obj.instance_variables.each do |var|
      val = @deep_cloning_obj.instance_variable_get(var)
      begin
	@deep_cloning = true
	val = val.deep_clone
      rescue TypeError
	next
      ensure
	@deep_cloning = false
      end
      @deep_cloning_obj.instance_variable_set(var, val)
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end
実行例
require 'clone'

class Foo
  def initialize
    @next = nil
  end
  attr_accessor :next
end

foo = Foo.new
foo.next = Foo.new
foo.next.next = foo
p foo                   # => #<Foo:0x100eda84 @next=#<Foo:0x100eda70 @next=#<Foo:0x100eda84 ...>>>
p foo.clone             # => #<Foo:0x100ed9f8 @next=#<Foo:0x100eda70 @next=#<Foo:0x100eda84 @next=#<Foo:0x100eda70 ...>>>>
p foo.deep_clone        # => #<Foo:0x100ed96c @next=#<Foo:0x100ed8e0 @next=#<Foo:0x100ed96c ...>>>
問題点
  • 組み込みクラスに未対応
  • 作業用のインスタンス変数が衝突する可能性がある
  • 組み込みクラスのように情報をインスタンス変数に保持していないクラスには対応できない(個別に対応する必要がある)

*1:Marshal.load( Marshal.dump(obj) ) を使えという事になっているが、Object#clone にはない制限がある。

2007-04-11

はてなダイアリーへようこそ!

このページはあなた専用の日記(ブログ)です。*1

さっそく「日記を書く」をクリックして最初の記事を書いてみましょう。

はてなダイアリーの一番簡単な使い方を知りたい方は、以下の動画をご覧ください。

D

(再生ボタンをクリックすると、はてなダイアリーの使い方を音声と動画で見ることができます。)

はてなダイアリーのヘルプでは、このような動画を交えた使い方の説明や、文字の色の付け方、本やDVDを紹介する「はてな記法」の使い方を解説しています。

より詳しいはてなダイアリーの使い方を知りたい方は、以下のヘルプをご覧ください。


それでは、日々の出来事やテレビ番組の感想、普段考えていることなど、あなたならではの日記を書いて楽しんでください!

*1:この文章はサンプルです。実際に自分の記事を書くときには削除しても大丈夫です。