Relaxe o vazio * fundição em C ++
-
27-09-2019 - |
Pergunta
Em C, não é um erro lançar ponteiros de e para void *
.
Um grande obstáculo em portar para C ++ é a necessidade de lançar ponteiros ao retornar de funções que lidam com indicadores genéricos, como malloc
, e funções declaradas em meu próprio código, como void *block_get(Blkno const blkno);
.
Meu código, no entanto, deve ser compilado por c e Compiladores C ++ com sucesso. Se eu fornecer moldes explícitos em todos os lugares por causa do C ++, eles devem ser elencos no estilo C e eu posso estar mascarando bugs devido ao lançamento de tipos de não-pontet para e para os tipos de ponteiros de ambos os idiomas.
Meu erro de referência é o seguinte:
struct Cpfs *cpfs = calloc(1, sizeof(*cpfs));
que no MSVC produz:
Erro 2 Erro C2440: 'Inicializando': Não é possível converter de 'void *' para 'cpfs *' e: src cpfs cpfs.c 179
Evidentemente eu não posso usar new
ou static_cast
que eu naturalmente eu usaria se não estivesse mais usando C. Qual é a melhor maneira de fornecer a segurança do tipo máximo em torno void *
Para cada idioma com verbosidade mínima?
Solução
Talvez algo assim? (não testado, nenhum compilador disponível, não usando macros com muita frequência):
#ifdef __cplusplus
#define pointer_cast(type, pointer) reinterpret_cast<type>(pointer)
#else
#define pointer_cast(type, pointer) (type)(pointer)
#endif
Outras dicas
Eu sugeriria simplesmente usar moldes de estilo C ou envolver o elenco em uma macro que se expande para nada (em c) ou um static_cast
em C ++.
Se o seu compilador suportar decltype()
, você pode usar um pouco de macro mágica para evitar ter que repetir explicitamente o nome do tipo (e, graças a sizeof
, o tamanho do elemento):
#ifdef __cplusplus
#define my_calloc(VAR, COUNT) \
static_cast<decltype(VAR)>(std::calloc(COUNT, sizeof *VAR))
#else
#define my_calloc(VAR, COUNT) calloc(COUNT, sizeof *VAR)
#endif
Exemplo de uso:
#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
struct Cpfs *cpfs = my_calloc(cpfs, 42);
A solução mais limpa provavelmente seria usar apenas um compilador C e vincular os arquivos de objeto, embora ...
Faça uma função de alocador de substituição que você possa definir de maneira diferente para as compilações C e C ++:- Algo assim em um arquivo de cabeçalho:
#ifdef __cplusplus
template<typename TypeT>
TypeT* MyAlloc(TypeT** pOut,size_t cb){
*pOut = static_cast<TypeT*>(malloc(cb)); //aint c++ pretty.
return *pOut;
}
#else
extern void* MyAlloc(void** ppv, size_t cb);
#endif
Agora você tem, nas compilações C ++, uma função que pode inferir o tipo de coisa que está lidando e, em C, é uma função regular que retorna um vazio*.
O único problema é a necessidade de passar no ponteiro para alocar - o compilador C ++ não tentará deduzir um parâmetro de modelo com base apenas no tipo de retorno de uma função AFAIK. Então você poderia chamá-lo de forma agnosticamente assim:-
int *p;
if(MyAlloc(&p,sizeof(int)*n)){
...
A única solução que conheço é fazer elenco explícito:
struct Cpfs *cpfs = (Cpfs*)calloc(1, sizeof(*cpfs));
Aqui, ambos os compiladores estão satisfeitos. Além disso, lembre -se de que, para compiladores mais antigos, Malloc pode devolver o char*.
hth
Mario