Problema com conversão de string para o readlink com C++

1. Problema com conversão de string para o readlink com C++

Nick Us
Nick-us

(usa Slackware)

Enviado em 11/07/2020 - 22:45h

Eu explico nos comentários dentro do código o que está estranho. Até onde entendi, o problema é que não estou sabendo converter uma std::string dentro da função readlink, porque os resultados são completamente estranhos e diferentes do que eu esperava. Visto que usando char funciona!

#include <iostream>
#include <unistd.h>
#include <string>

int main() {
char Path1[501];
readlink("/proc/self/exe", Path1, 500);
std::cout << Path1 << '\n'; // RESULT OK: /home/nick/Desktop/Encyclopedia/Test

// SE As linhas acima estiverem comentadas, o Resultado abaixo será:
// ERRADO! De onde saiu esse @ ???
// /home/nick/Desktop/Encyclopedia/[email protected]

// Se eu deixar todos os comandos rodando como está agora, fica ainda mais estranho
// ERRADO! Que caminho louco é esse???
// /home/nick/Desktop/Encyclopedia/Testncyclopedia/Test

std::string Path2;
readlink("/proc/self/exe", (char*)Path2.c_str(), 500); // Assim ou da forma abaixo o RESULT é o mesmo!
//readlink("/proc/self/exe", const_cast<char*>(Path2.c_str()), 500);

// Conforme explico acima, isso me retorna 2 valores diferentes, mudam se eu comentar as 3 primeiras linhas do programa ou se deixar os 2 comandos readlink
std::cout << Path2.c_str() << '\n';
}

Eu tenho o mesmo problema com getcwd e talvez até outros comandos. Ainda não consigo converter direito std::string para char, const char e etc...


  


2. Re: Problema com conversão de string para o readlink com C++

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/07/2020 - 06:52h

Salve, Nick.

Começando pelo fim, note que você não tem conversões de std::string para char ou const char, pois estes dois só pode ser valores que indicam um único caráter. Strings são cadeias de caracteres, que precisam de um array de caracteres ou array de caracteres constantes. Por causa da forma como arrays funcionam em C (que foi herdada pelo C++), tais arrays aparecem em expressões como ponteiros para seus primeiros elementos (i.e. ponteiro para caráter ou ponteiro para caráter constante, respectivamente).

Note também que uma função como std::string::c_str() retorna um valor que É do tipo “ponteiro para caracteres constantes”. O ponteiro faz parte do tipo do valor; não é um valor do tipo const char magicamente envelopado com um ponteiro, nem coisa parecida.

E também não existe conversão quando você usa std::string::c_str(). O que existe realmente é que o objeto do tipo std::string tem um array de caracteres interno (possivelmente alocado dinamicamente, mas isso é um detalhe interno da implementação, e o propósito da classe é justamente fazer com que você não tenha de se preocupar com isso), e a função-membro c_str() simplesmente retorna um ponteiro para o primeiro caráter desse array interno do objeto.

Devolver o valor de um ponteiro interno do objeto geralmente é considerado um problema de projeto da classe, pois viola a característica normalmente desejada de encapsulamento de dados. De fato, se você procurar on line, provavelmente vai encontrar muitas críticas ao projeto de std::string, incluindo a existência da função membro c_str(). Entretanto, essa função é uma solução de compromisso, destinada a permitir que você use objetos dessa classe para armazenar strings que possam ser usadas em expressões que esperariam strings ao estilo do C (i.e. ponteiros para o primeiro elemento de um array de caracteres, no qual um byte nulo indica onde a string termina).

Parte da solução de compromisso é fazer com que o ponteiro devolvido aponte para caracteres constantes, de modo a reduzir a tentação de alguém usar tais ponteiros de forma perigosa, como é o caso de um programa que tenta gravar conteúdo nesse array interno sem nem mesmo tentar saber se esse array tem espaço suficiente para receber tal conteúdo.

Desnecessário dizer que esse é justamente o caso do seu programa, certo?

Soluções ordinárias para isso?

A mais ordinária é usar realmente um array de caracteres (nativo ou dinamicamente alocado) e operar do mesmo jeito como se operaria em C, e depois construir ou copiar para dentro do objeto os dados dessa string no estilo do C.

Outra possibilidade é fazer algo parecido com o que você tentou fazer, mas apenas depois de garantir que existe espaço suficiente no array interno do objeto, e depois ajustar o tamanho da string contida no objeto, para que ela coincida com o tamanho dos dados que foram forçados para dentro dela por meio da quebra de encapsulamento.
std::string Path2(PATH_MAX+1, '\0');  // Preenche string com bytes nulos, que seriam marcas de fim de string em C.  PATH_MAX obtido em <climits> ou <limits.h>.
if(readlink("/proc/self/exe", const_cast<char *>(Path2.c_str()), Path2.size()-1)==-1){
cerr << "Não foi possível examinar link simbólico: " << strerror(errno) << ".\n";
exit(1);
}
Path2.resize(strlen(Path2.c_str());


Ainda outra opção é usar a biblioteca de operações em sistemas de arquivos que foi adotada pelo padrão do C++ de 2017. Ela tem uma interface padrão, que não depende do sistema operacional. Desse modo, você pode usar std::filesystem::current_path() em qualquer sistema, em vez de preocupar de usar getcwd() no mundo POSIX e _getcwd() ou _wgetcwd() no Windows, ou std::filesystem::read_symlink(), em lugar de readlink() no mundo POSIX e, no Windows, da tripa de GetFileAttributes()/teste de dois atributos/CreateFile()/GetFinalPathNameByHandle().

O problema com std::filesystem do C++17 é que ela também não usa std::string para representar caminhos de arquivos e diretórios, mas sim std::filesystem::path, que inclui funções-membro úteis para extrair e manipular informações sobre as partes que compõem um caminho. Por outro lado, pode ser construída a partir de std::strings ou de strings do C (ponteiro para caracteres constantes) e pode produzir std::string (e outros tipos de strings do C++) a partir da representação interna do nome do caminho.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner
Linux banner
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts