Contexto : Eu tenho uma fila que suporta leitura única/gravação única de dois threads diferentes (/ou não), para impor esse comportamento, ou seja, leitor único/gravador único por vez, preciso limitar o número de threads proprietários a fila por vez para 2 (o escritor já possui a fila), eu estava pensando em criar um shared_ptr para a fila com uma contagem máxima de referências conhecida em tempo de compilação definida como 2. Portanto, minha pergunta é a seguinte.
Pergunta : Existe uma maneira de implementar um ponteiro compartilhado (talvez usando unique_pointer
's) com uma contagem máxima de referências conhecida em tempo de compilação? Meu caso é, max_ref_count = 2
ou seja, exceder o limite de contagem de referências = 2 deve ser um erro em tempo de compilação.
const auto p = std::make_shared<2, double>(2159); //works fine
const auto q = p; // works fine
const auto err1 = p; // does not compile
const auto err2 = q; // does not compile
Impossível em tempo de compilação
O que você está fazendo não é possível em tempo de compilação, apenas em tempo de execução. Observe que
std::shared_ptr
é copiável, portanto é possível que "os caminhos de cópia diverjam":Neste ponto,
B
nãoC
sabemos nada um sobre o outro e não há como garantir que o façam através do sistema de tipos. Na melhor das hipóteses, você pode contar o comprimento do caminho da cópia atéA
asB
cópias subsequentes, mas não a quantidade global de cópias.Isso exigiria alguma forma de metaprogramação com estado e C++ não suporta isso.
Difícil em tempo de execução
Observe dois problemas:
std::shared_ptr
também se espera que tenha contagem segura de threads. Isto também significa questd::shared_ptr::use_count()
pode não produzir o resultado mais recente e não pode ser considerado uma métrica confiável.int
além do contador atômico que os ponteiros compartilhados já possuem. Isso é um pouco chato, mas factível.Solução de thread único
Se você não considerar os problemas do multithreading, poderá apenas usar
std::shared_ptr::use_count()
para controlar os usos. Esta é uma métrica confiável em um programa de thread único. Você só precisa fazer um wrapper parastd::shared_ptr
o qual será lançado sempre que o limite for excedido, o que pode acontecer no construtor de cópia e no operador de atribuição de cópia.Solução multithread
Isso é um pouco mais complicado e fornecerei apenas o esboço geral.
Você pode criar um
std::shared_ptr
com um deleter personalizado. Este eliminador personalizado pode conter umstd::atomic<std::size_t>
para manter um controle confiável da contagem de uso.Com isso, você não precisa gerenciar recursos sozinho, mas pode deixar
std::shared_ptr
isso e acessar o deletor abase.get_deleter<counting_deleter>()
qualquer momento.No construtor de cópia, semelhante à solução single-threaded, você verificaria:
Basicamente, verificamos se é possível aumentar a contagem de uso. Caso contrário, lançamos, caso contrário, tentamos incrementar
use_count
withcompare_exchange_weak
.O operador de atribuição de cópia é análogo. As outras funções-membro especiais podem ser padronizadas, uma vez que mover ou destruir não pode ultrapassar o limite.
O padrão std::shared_ptr em C++ não fornece diretamente um mecanismo para definir uma contagem máxima de referência em tempo de compilação. No entanto, você pode obter um comportamento semelhante criando um ponteiro inteligente personalizado com uma contagem de referência limitada. Aqui está um exemplo básico: