Evite ter um método de inicialização

12

Eu tenho esse código existente onde eles têm uma classe e um método de inicialização nessa classe. Espera-se que, uma vez que o objeto da classe seja criado, eles precisem chamar a inicialização.

Razão pela qual o método de inicialização existe O objeto é criado cedo para ter um escopo global e, em seguida, o método de inicialização é chamado posteriormente após o carregamento de uma dll da qual depende.

Problema ao inicializar A classe agora possui esse bool isInitialized, que precisa ser verificado em todos os métodos antes de prosseguir e retornar erro se não for inicializado. Simplificando, é uma grande dor.

Uma solução possível Inicialize no construtor. Tenha apenas um ponteiro para o objeto no escopo global. Crie o objeto real após o carregamento da DLL.

Problema com a solução acima Qualquer pessoa que crie um objeto dessa classe precisa saber que precisa ser criada somente após o carregamento da DLL, caso contrário, falhará.

Isso é aceitável?


fonte
Eu tive esse mesmo problema ao criar objetos relacionados OpenGL que precisam ser instanciado antes existe um contexto OpenGL, mas é suposto para segurar objetos dependentes OpenGL como calllists, texturas, etc.
3
Pergunta estúpida # 1: Por que o construtor de objetos não pode carregar a DLL se ela ainda não está carregada?
22911 John R. Strohm
1
Peça desculpas por ressuscitar uma pergunta antiga, mas caso alguém esteja lendo isso no ano de 2017, use o call_onceC ++ 11 . Projetos que ainda não estão no C ++ 11 devem estudar como o call_once é implementado no C ++ 11 (foco no problema que ele resolve e depois como) e, em seguida, reimplementá-lo no seu sabor (obsoleto) de C ++. Ele precisa de uma primitiva de sincronização segura multithread, cujo estado precisa ser inicializado estaticamente (com um valor constante). Observe que os compiladores anteriores ao C ++ 11 podem ter outras idiossincrasias que precisam ser satisfeitas.
Rwong 14/05

Respostas:

7

Parece um trabalho para um proxy virtual.

Você pode criar um proxy virtual contendo uma referência ao objeto em questão. Enquanto a DLL não estiver carregada, o Proxy poderá oferecer um certo comportamento padrão aos clientes; assim que a DLL for carregada, o proxy simplesmente encaminhará todas as solicitações para o assunto real.

O proxy virtual será responsável por verificar a inicialização da DLL e, com base nisso, decide se a solicitação deve ser delegada ao assunto real.

Padrão de proxy na Wikipedia

Como você gosta dessa ideia?

edalorzo
fonte
você realmente não resolve muito aqui. para cada método adicionado no objeto real, você também precisará adicionar esse método ao proxy. Não?
Isso evita o problema de lembrar de chamar o init. , mas parece que todas as funções no proxy ainda precisam verificar se o .dll está carregado.
1
@Sriram agora há uma grande diferença: ao usar um proxy, você restringiu a manutenção relacionada à verificação de DLL a uma única classe: o proxy. Os clientes da classe nem precisam saber sobre a verificação de DLL. Como a maioria dos métodos fará delegação, não vejo muitos problemas em ter que implementar a interface no assunto real no proxy. Em outras palavras, você pode impedir a criação do assunto real até ter certeza de que a DLL foi carregada e não precisará verificar o estado da DLL todas as vezes.
@edalorzo: enquanto a idéia é boa, a questão aqui é que já estamos a falar de um proxy, eo OP está se queixando de ter que verificar em cada método de sua procuração ...
Matthieu M.
4

Nada útil ocorre se a DLL ainda não estiver carregada; o objeto gera apenas um erro. É um erro fatal? Como o erro é tratado?

Sua metodologia de teste garante que esse erro nunca ocorra no produto acabado?

Parece um trabalho para testes, não para projetos de arquitetura. Não basta retornar um erro, assertpois isso nunca acontece.

Corrija todos os clientes da classe que atualmente capturam e ignoram o erro para não tentar causá-lo em primeiro lugar.

Um padrão multithread pode ser definir uma variável de condição compartilhada após o carregamento da DLL e aguardar o construtor do objeto (e bloquear outros threads) até que a DLL seja carregada.

Potatoswatter
fonte
Acho que você está certo, não há nada de errado em lançar uma exceção na criação do objeto em questão se a DLL não tiver sido inicializada corretamente. O código do cliente precisaria lidar apenas com essa exceção e o teste deve garantir que a exceção seja tratada adequadamente.
2

Primeira coisa: evite objetos globais como pragas, a menos que seu estado nunca mude (configuração).

Agora, se você estiver preso, por qualquer motivo, existem vários projetos que provavelmente podem ajudá-lo.

Eu vim com duas idéias:

  1. Use uma fachada para carregar a DLL. O objeto só pode ser acessado através da fachada, a fachada carrega a DLL na criação e instancia o objeto ao mesmo tempo.

  2. Use um proxy, mas o tipo inteligente;)

Deixe-me explicar o segundo ponto, pois temo que a resposta de @edalorzo possa ter assustado você:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Agora você tem apenas uma verificação.

Isso também pode ser feito através de algum tipo de ponteiro inteligente:

Pointer<Object> o;

Aqui você reserva apenas espaço para um ponteiro, inicialmente nulo, e somente quando a DLL é carregada você realmente aloca o objeto. Todos os acessos anteriores devem gerar uma exceção (NullException: p?) Ou encerrar o programa (corretamente).

Matthieu M.
fonte
1

Esta é uma pergunta complicada. Eu sinto que a arquitetura pode ajudar com o problema.

A partir das evidências, parece que o .dll não é carregado quando o programa é carregado. Parece quase um plugin.

Uma coisa que vem à mente é que você precisa inicializar a DLL. O programador deve assumir que o objeto não retornará de maneira confiável, de qualquer maneira. Você pode tirar proveito disso ( bool isObjectLoaded() { return isInitialized; }), mas definitivamente obterá mais relatórios de erros nas suas verificações.

Eu estava pensando em um singleton.

Se você chamar o singleton, ele retornará o objeto correto se puder recuperar um. Se ele não puder retornar o objeto correto, você deverá obter algum valor de erro / nullptr / objeto base vazio. Isso não funcionaria se o objeto pudesse morrer em você.

Além disso, se você precisar de várias instâncias do objeto, poderá usar algo como um Factory.

Joe Plante
fonte