TL; DR
- (ideia pessoal, não comprovada)
- "Este
cptr
é const
, esse mptr
não é
- Idéia quase comprovada, além do design
Mat_<T>
do OpenCV
- Asse a constância no parâmetro de modelo
T
e
- Certifique-se de que todos os furos estejam conectados, fazendo algumas alterações no OpenCV.
É difícil, e mesmo bibliotecas de imagens conhecidas, como o OpenCV, não encontram uma solução perfeita. Em vez disso, a maioria das pessoas viveria com o pragmatismo, ou seja, estaria satisfeita com o que quer que tenha conseguido.
Aqui está a minha ideia não comprovada:
- Em cada classe
Image
(e Buffer
class também), mantenha dois campos de ponteiro brutos:
- Um é um ponteiro não-const void (ou
uchar
).
- Um é um ponteiro const void (ou
uchar
). (Significa que não permite modificar os dados que estão sendo apontados, através deste ponteiro.)
- Em
Image
instâncias que precisam "reforçar a constância", mesmo que alguém tenha conseguido obter uma referência não-qualificada a esse Image
, o ponteiro não const é intencionalmente configurado como nullptr
.
- Antes de qualquer modificação de pixel, alguém terá que validar o ponteiro antes de prosseguir. Normalmente, se alguém pretende modificar um monte de pixels, um só precisa validar o ponteiro uma vez, portanto, com design adequado, isso não deve se tornar uma sobrecarga de desempenho.
- Todas as modificações serão feitas através do ponteiro não const e todas as operações somente leitura serão feitas através do ponteiro const.
- Para imagens mutáveis (ou imagens que permitem modificações), os dois ponteiros sempre terão o mesmo valor.
Esta é apenas uma ideia selvagem e não comprovada. Sinta-se livre para explorar e criticar. Dito isto, a minha ideia é fabricada a partir do uso prolongado do OpenCV e de implementações de processamento de imagem de baixo nível.
Como um bônus, se você já estiver familiarizado com o OpenCV, você pode tentar as duas declarações a seguir:
const auto sz = cv::Size(3, 3);
const uchar fillval = 0U;
cv::Mat_<uchar> matMutable1B(sz, fillval);
cv::Mat_<const uchar> matImmutable1B(sz, fillval);
Observe que o primeiro permite modificações através do estilo usual do OpenCV, por exemplo mat.at(row, col) = fillval;
, mat(row, col) = fillval;
, mat.ptr(row)[col] = fillval;
, enquanto o segundo não permite.
Isso ocorre porque o parâmetro de modelo <T>
está qualificado pela segunda declaração.
No entanto, atualmente, isso não é infalível, porque alguém que obtiver o ponteiro via mat.data
obterá um uchar*
, independentemente da qualificação const do parâmetro de modelo <T>
.
Uma distinção muito importante sobre immutability / object state access control
versus C++ style notational const-qualification
C ++ const-qualificação afeta código que "vem por / obter" uma referência a uma determinada classe. Assim, ele passa pela const-qualificação por algo como uma cadeia de custódia de tipos. O Image
em si não pode descobrir se alguém possuir uma referência a ele e fazer uma chamada para ele é const-qualified ou não. Em vez disso, o C ++ aplica isso ao verificar o código do chamador e bloqueia a tentativa do chamador. A classe Image
nunca sabe.
Minha idéia de usar dois ponteiros (const / non-const) e definir o non-const como nullptr é uma tentativa de resolver esse problema em tempo de execução. Assim, alguém que obtiver um Image
que não seja classificado como const, mas seu ponteiro de dados não const for nullptr, enfrentará conseqüências de tempo de execução.
Em última análise, se isso for muito difícil de discutir, talvez gaste algum tempo com o OpenCV e tente tanto const-qualify a matriz em si, quanto o parâmetro template, para ver qual técnica atende ao máximo de suas necessidades.
Outra lição aprendida com o OpenCV é que você deve implementar seu próprio mecanismo de contagem de referências na classe image. Não fazer isso torna o código do usuário da biblioteca muito frágil.