Sobrecarregando operator de saida c++[RESOLVIDO]

1. Sobrecarregando operator de saida c++[RESOLVIDO]

Jean César
dark777

(usa Slackware)

Enviado em 07/12/2017 - 20:18h

Pessoal estou com o seguinte codigo abaixo que converte numeros inteiros em binario

#include <algorithm> // reverse
#include <climits> // CHAR_BIT
#include <iostream>
#include <sstream>

struct Bin
{
int b;
friend std::ostream& operator<<(std::ostream& os, const Bin& f);

std::string binary(unsigned int n)
{
b=n;
//converte numero para string de bits
std::stringstream bitsInReverse;
//int nBits = sizeof(b) * CHAR_BIT;
unsigned int nBits = sizeof(b) * 3;

while (nBits-- > 0)
{
bitsInReverse << (b & 1);
b >>= 1;
}

std::string bits(bitsInReverse.str());
std::reverse(bits.begin(), bits.end());
return bits;
}
}bin;


std::ostream &operator<<( std::ostream &saida, const Bin& f)
{
saida << f.binary(f.b);
return saida;
}

int main()
{
//127 em binario 000001111111
std::cout << "\n\t127 em hexadecimal: " << std::hex << 127
<< "\n\t127 em octal: " << std::oct << 127
<< "\n\t127 em decimal: " << std::dec << 127
<< "\n\t127 em binario: " << bin << 127
<<"\n\n";
}


se eu fizer const Bin& f ao compilar retorna o seguinte erro abaixo:

operatorBin.cxx: In function ‘std::ostream& operator<<(std::ostream&, const Bin&)’:
operatorBin.cxx:54:23: error: passing ‘const Bin’ as ‘this’ argument discards qualifiers [-fpermissive]
saida << f.binary(f.b);
^
operatorBin.cxx:31:13: note: in call to ‘std::string Bin::binary(unsigned int)’
std::string binary(unsigned int n)

agora se eu fizer Bin& f;

ele nao da o erro acima compila tranquilamente mas, ao executar ele nao converte o numero usando o operator sobrecarregado..
O que esta dando de errado?


  


2. Re: Sobrecarregando operator de saida c++[AJUDA]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/12/2017 - 00:44h

Problema de fundo: um ponteiro constante não pode ser convertido implicitamente para ponteiro não-constante, pois isso violaria garantias do sistema de tipos.

Como você provavelmente já sabe, toda função-membro não-estática de uma classe recebe um ponteiro para o objeto que a invocar. Esse ponteiro pode ser referido no corpo da função pelo nome this, que na prática funciona como se fosse um parâmetro oculto da função, obtido implicitamente a partir do objeto.

Numa função-membro não-qualificada (explico a seguir), o tipo do ponteiro this é "ponteiro para o tipo da classe". Então, se eu tenho o objeto x, do tipo X, e chamo a função-membro X::func() sobre o objeto x através da forma x.func(), a função vai receber pelo ponteiro this o valor de &x, cujo tipo é X*.

Contudo, se o objeto x tiver sido declarado como constante, o tipo de &x não será X*, mas sim const X*, que não pode ser convertido para o X* que X::func() esperaria receber, pois isso provocaria uma violação das garantias do sistema de tipos (imagine se a função tentasse, internamente, modificar o objeto apontado por this).

Para poder chamar funções-membros em objetos constantes, tais funções têm de ser qualificadas como constantes. Isso faz com que o tipo do ponteiro this usado por ela internamente seja do tipo const X*. Essas funções podem também ser usadas com objetos não-constantes, uma vez que é seguro que um ponteiro constante aponte para um objeto não-constante (o contrário é que não pode).

A forma de declarar uma função-membro qualificada segue a forma tradicional do C de se expressar da direita para a esquerda.

Segue abaixo o exemplo de uma classe com uma função não qualificada (não-constante e não-volátil) e outra qualificada como constante.

class X {
private:
std::string _str;

public:
void str(const std::string &nova_str){
// Altera a string. Função exige objeto não-constante.
_str=nova_str;
}

std::string str() const { // <--- Note o “const” após a lista de parâmetros.
// Retorna uma cópia da string. Não altera o objeto, que pode ser constante ou não.
return _str; // Gera uma cópia do valor de _str.
}
};


Para resolver o seu problema, basta que você qualifique como constantes todas as funções que não precisem alterar o conteúdo do objeto. Essa é, inclusive, uma boa prática.


3. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Jean César
dark777

(usa Slackware)

Enviado em 08/12/2017 - 16:36h

paulo1205 escreveu:

Problema de fundo: um ponteiro constante não pode ser convertido implicitamente para ponteiro não-constante, pois isso violaria garantias do sistema de tipos.

Como você provavelmente já sabe, toda função-membro não-estática de uma classe recebe um ponteiro para o objeto que a invocar. Esse ponteiro pode ser referido no corpo da função pelo nome this, que na prática funciona como se fosse um parâmetro oculto da função, obtido implicitamente a partir do objeto.

Numa função-membro não-qualificada (explico a seguir), o tipo do ponteiro this é "ponteiro para o tipo da classe". Então, se eu tenho o objeto x, do tipo X, e chamo a função-membro X::func() sobre o objeto x através da forma x.func(), a função vai receber pelo ponteiro this o valor de &x, cujo tipo é X*.

Contudo, se o objeto x tiver sido declarado como constante, o tipo de &x não será X*, mas sim const X*, que não pode ser convertido para o X* que X::func() esperaria receber, pois isso provocaria uma violação das garantias do sistema de tipos (imagine se a função tentasse, internamente, modificar o objeto apontado por this).

Para poder chamar funções-membros em objetos constantes, tais funções têm de ser qualificadas como constantes. Isso faz com que o tipo do ponteiro this usado por ela internamente seja do tipo const X*. Essas funções podem também ser usadas com objetos não-constantes, uma vez que é seguro que um ponteiro constante aponte para um objeto não-constante (o contrário é que não pode).

A forma de declarar uma função-membro qualificada segue a forma tradicional do C de se expressar da direita para a esquerda.

Segue abaixo o exemplo de uma classe com uma função não qualificada (não-constante e não-volátil) e outra qualificada como constante.

class X {
private:
std::string _str;

public:
void str(const std::string &nova_str){
// Altera a string. Função exige objeto não-constante.
_str=nova_str;
}

std::string str() const { // <--- Note o “const” após a lista de parâmetros.
// Retorna uma cópia da string. Não altera o objeto, que pode ser constante ou não.
return _str; // Gera uma cópia do valor de _str.
}
};


Para resolver o seu problema, basta que você qualifique como constantes todas as funções que não precisem alterar o conteúdo do objeto. Essa é, inclusive, uma boa prática.




wiki.anon

fiz o que vc disse mas ainda assim ele nao esta convertendo em binario ele da toda saida em 00000000000


4. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 11/12/2017 - 11:41h

O problema é com seu algoritmo de conversão. Tente ver de onde ele está recebendo o valor a ser convertido, e você verá que ele não chega de lugar nenhum.


5. Re: Sobrecarregando operator de saida c++[AJUDA]

Jean César
dark777

(usa Slackware)

Enviado em 12/12/2017 - 13:44h

paulo1205 escreveu:

O problema é com seu algoritmo de conversão. Tente ver de onde ele está recebendo o valor a ser convertido, e você verá que ele não chega de lugar nenhum.



mais é estranho pois eu uso o mesmo algoritimo de conversao em uma função e converte corretamente, mas quando coloco no operator ele nao o faz..

wiki.anon


6. Re: Sobrecarregando operator de saida c++[AJUDA]

Fernando
phoemur

(usa FreeBSD)

Enviado em 12/12/2017 - 22:03h

você não poderia utilizar std::bitset ( http://en.cppreference.com/w/cpp/utility/bitset ) para facilitar ?

por exemplo:

arquivo binary.cpp

//g++ -O2 -Wall -Wpedantic -std=c++11 -o binary binary.cpp
#include <iostream>
#include <string>
#include <bitset>
#include <type_traits>

template<typename T>
typename std::enable_if<std::is_integral<T>::value, std::string>::type
binary(const T& n){
return std::bitset<sizeof(T)*8>(n).to_string();
}

int main()
{
std::cout << binary(127) << std::endl;

return 0;
}


O único porém do bitset é que o tamanho deve ser conhecido no tempo de compilação, porém do jeito que eu fiz com o template e o sizeof vai dar certo...
Aquela especialização do template ali é apenas para fazer com que a função aceite apenas inteiros...


7. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Fernando
phoemur

(usa FreeBSD)

Enviado em 13/12/2017 - 10:18h

dark777 escreveu:

paulo1205 escreveu:

O problema é com seu algoritmo de conversão. Tente ver de onde ele está recebendo o valor a ser convertido, e você verá que ele não chega de lugar nenhum.



mais é estranho pois eu uso o mesmo algoritimo de conversao em uma função e converte corretamente, mas quando coloco no operator ele nao o faz..

wiki.anon


Sendo mais explicito, seu problema é que você não está criando uma struct Bin para converter, está passando direto um inteiro 127, veja:

std::cout << "\n\t127 em hexadecimal: " << std::hex << 127
<< "\n\t127 em octal: " << std::oct << 127
<< "\n\t127 em decimal: " << std::dec << 127
<< "\n\t127 em binario: " << bin << 127
<<"\n\n";



8. Re: Sobrecarregando operator de saida c++[AJUDA]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 13/12/2017 - 10:37h

Caro dark777,

O problema que você está tendo é que o objeto bin é construído sem um valor especificado para o campo b, o que está fazendo com que ele assuma o valor 0. Quando, posteriormente, você chama a função-membro binary(), ela vai usar esse valor de b. O valor 127, que aparece depois de o objeto bin ter sido passado a operator<<(std::ostream &, Bin &) não afeta o objeto bin de modo nenhum, mas é tão-somente aplicado ao objeto retornado por essa função, que é a própria referência a std::ostream que lhe serviu como parâmetro, por meio do operador <<.


Você aparentemente quer algo parecido com os manipuladores std::hex, std::dec e std::oct. Contudo, esses manipuladores alteram campos internos do objeto std::ostream que, por sua vez, são herdados de std::ios_base. Esses campos internos, por definição, dão ao objeto ios_base a capacidade de tratar apenas as base 16, 10 e 8, respectivamente.

Como você provavelmente não quer ter de mexer internamente na implementação de ios_base, sua solução tem de ser por meio de wrappers.

O wrapper mais óbvio seria uma função (ou objeto-função, também conhecido como functor) que recebesse um inteiro e devolvesse uma string com a representação binária desse inteiro. No código, isso ficaria com a seguinte forma.

std::cout
<< std::dec << 127 << '\n' // Primeiro imprime o nº em decimal
<< binary(127) << '\n' // e depois em binário.
;


Isso funcionaria, mas não ficaria com cara de manipulador.

Se você fizer muita questão de algo mais parecido com um manipulador, até dá para fazer, mas com diversas limitações, e a sua classe precisará interceptar tanto o stream de saída quanto o parâmetro a ser impresso. Um esqueleto de ideia, não testado e que certamente pode ser muito mais elaborado, segue abaixo.

#include <ostream>
#include <stdexcept>
#include <string>
#include <cstdint>

using namespace std;

class my_binary_conversion_t {
private:
ostream *p_out;
friend my_binary_conversion_t &operator<<(ostream &, my_binary_conversion_t &);

public:
my_binary_conversion_t(): p_out(nullptr) { }

ostream &operator<<(uintmax_t val){
if(!p_out)
throw logic_error("manipulator not tied to any output stream");

string s;
// Faz com que ‘s’ receba a representação binária de ‘val’ — pode ser
// usando a sugestão do phoemur ou alguma outra técnica.

ostream &out=*p_out;
p_out=nullptr; // Limpa o ponteiro, para evitar que se use apenas o manipulador, em lugar do stream.
return out << s;
}
} bin;

my_binary_conversion_t &operator<<(ostream &out, my_binary_conversion_t &wrapper){
wrapper.p_obj=&out;
return wrapper; // Note que eu retorno uma referência para o wrapper, não para o stream.
}



9. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Jean César
dark777

(usa Slackware)

Enviado em 13/12/2017 - 17:19h

phoemur escreveu:

você não poderia utilizar std::bitset ( http://en.cppreference.com/w/cpp/utility/bitset ) para facilitar ?

por exemplo:

arquivo binary.cpp

//g++ -O2 -Wall -Wpedantic -std=c++11 -o binary binary.cpp
#include <iostream>
#include <string>
#include <bitset>
#include <type_traits>

template<typename T>
typename std::enable_if<std::is_integral<T>::value, std::string>::type
binary(const T& n){
return std::bitset<sizeof(T)*8>(n).to_string();
}

int main()
{
std::cout << binary(127) << std::endl;

return 0;
}


O único porém do bitset é que o tamanho deve ser conhecido no tempo de compilação, porém do jeito que eu fiz com o template e o sizeof vai dar certo...
Aquela especialização do template ali é apenas para fazer com que a função aceite apenas inteiros...



A questão é fazer sobrecarga de operator para que converta ja fiz um usando bitset alias tenho um algoritmo que faz varias conversoes apenas usando a std::dec, hex, oct e bitset mas estava tentando fazer a conversão atravez de um operator...
wiki.anon


10. Re: Sobrecarregando operator de saida c++[AJUDA]

Jean César
dark777

(usa Slackware)

Enviado em 13/12/2017 - 17:38h

paulo1205 escreveu:

Caro dark777,

O problema que você está tendo é que o objeto bin é construído sem um valor especificado para o campo b, o que está fazendo com que ele assuma o valor 0. Quando, posteriormente, você chama a função-membro binary(), ela vai usar esse valor de b. O valor 127, que aparece depois de o objeto bin ter sido passado a operator<<(std::ostream &, Bin &) não afeta o objeto bin de modo nenhum, mas é tão-somente aplicado ao objeto retornado por essa função, que é a própria referência a std::ostream que lhe serviu como parâmetro, por meio do operador <<.


Você aparentemente quer algo parecido com os manipuladores std::hex, std::dec e std::oct. Contudo, esses manipuladores alteram campos internos do objeto std::ostream que, por sua vez, são herdados de std::ios_base. Esses campos internos, por definição, dão ao objeto ios_base a capacidade de tratar apenas as base 16, 10 e 8, respectivamente.

Como você provavelmente não quer ter de mexer internamente na implementação de ios_base, sua solução tem de ser por meio de wrappers.

O wrapper mais óbvio seria uma função (ou objeto-função, também conhecido como functor) que recebesse um inteiro e devolvesse uma string com a representação binária desse inteiro. No código, isso ficaria com a seguinte forma.

std::cout
<< std::dec << 127 << '\n' // Primeiro imprime o nº em decimal
<< binary(127) << '\n' // e depois em binário.
;


Isso funcionaria, mas não ficaria com cara de manipulador.

Se você fizer muita questão de algo mais parecido com um manipulador, até dá para fazer, mas com diversas limitações, e a sua classe precisará interceptar tanto o stream de saída quanto o parâmetro a ser impresso. Um esqueleto de ideia, não testado e que certamente pode ser muito mais elaborado, segue abaixo.

#include <ostream>
#include <stdexcept>
#include <string>
#include <cstdint>

using namespace std;

class my_binary_conversion_t {
private:
ostream *p_out;
friend my_binary_conversion_t &operator<<(ostream &, my_binary_conversion_t &);

public:
my_binary_conversion_t(): p_out(nullptr) { }

ostream &operator<<(uintmax_t val){
if(!p_out)
throw logic_error("manipulator not tied to any output stream");

string s;
// Faz com que ‘s’ receba a representação binária de ‘val’ — pode ser
// usando a sugestão do phoemur ou alguma outra técnica.

ostream &out=*p_out;
p_out=nullptr; // Limpa o ponteiro, para evitar que se use apenas o manipulador, em lugar do stream.
return out << s;
}
} bin;

my_binary_conversion_t &operator<<(ostream &out, my_binary_conversion_t &wrapper){
wrapper.p_obj=&out;
return wrapper; // Note que eu retorno uma referência para o wrapper, não para o stream.
}


Interessante porém estava pesquisando em outros forums e consegui chegar a este resultado

#include <ios> //ios_base
#include <climits> // CHAR_BIT
#include <sstream>
#include <iostream>
#include <algorithm> // reverse

struct BinStream
{
std::string binario(unsigned int n)
{
//converte numero para string de bits
std::stringstream bitsInReverse;
//int nBits = sizeof(n) * CHAR_BIT;
unsigned int nBits = sizeof(n)*2.5;

while (nBits-- > 0)
{
bitsInReverse << (n & 1);
n >>= 1;
}

std::string bits(bitsInReverse.str());
std::reverse(bits.begin(), bits.end());
return bits;
}

std::ostream& os;

BinStream(std::ostream& os): os(os) {}


template<class T>
BinStream& operator<<(T& value)
{
os << value;
return *this;
}

BinStream& operator<<(int value)
{
os << binario(value);
return *this;
}
/*
std::ostream& operator<<(std::ios_base& (__cdecl *_Pfn)(std::ios_base&))
{
return os <<_Pfn;
}
*/
};


struct Bin
{
friend BinStream operator<<(std::ostream& os, const Bin& f);
}bin;


BinStream operator<<(std::ostream& os, const Bin& f)
{
return BinStream(os);
}






int main()
{
std::cout << "\n\t127 em decimal: " << std::dec << 127
<< "\n\t127 em octal: " << std::oct << 127
<< "\n\t127 em hexadecimal: " << std::hex << 127
<< "\n\t127 em binario: " << bin << 12 << "\n\n";
}


só que esta dando erro nesta parte:

  std::ostream& operator<<(std::ios_base& (__cdecl *_Pfn)(std::ios_base&))
{
return os <<_Pfn;
}


error: expected ‘,’ or ‘...’ before ‘(’ token
std::ostream& operator<<(std::ios_base& (__cdecl *_Pfn)(std::ios_base&))
^
operatorBin2.cxx: In member function ‘std::ostream& std::BinStream::operator<<(std::ios_base&)’:
operatorBin2.cxx:49:16: error: ‘_Pfn’ was not declared in this scope
return os <<_Pfn;


pois ela impede que sobrescreva todas as outras conversoes por ex:

  std::cout << "\n\t127 em binario:     " << bin << 127
<< "\n\t127 em octal: " << std::oct << 127
<< "\n\t127 em hexadecimal: " << std::hex << 127
<< "\n\t127 em decimal: " << std::dec << 127 << "\n\n";


        127 em binario:     0001111111
127 em octal: 0001111111
127 em hexadecimal: 0001111111
127 em decimal: 0001111111


Entao eu poderia fazer isso:

std::cout  << "\n\t127 em binario:     " << bin << 127
<< "\n\t127 em octal: " << std::oct << 127
<< "\n\t127 em binario: " << bin << 127
<< "\n\t127 em hexadecimal: " << std::hex << 127
<< "\n\t127 em binario: " << bin << 127
<< "\n\t127 em decimal: " << std::dec << 127
<< "\n\t127 em binario: " << bin << 127 << "\n\n";*/


        127 em binario:     0001111111
127 em octal: 177
127 em binario: 0001111111
127 em hexadecimal: 7f
127 em decimal: 127
127 em binario: 0001111111



wiki.anon


11. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 15/12/2017 - 01:14h

dark777 escreveu:

Interessante porém estava pesquisando em outros forums e consegui chegar a este resultado


Minha resposta anterior foi no sentido de induzir você a uma solução. Ela, por si só, funcionava (embora eu tenha omitido especificamente a conversão para binário, em si), mas não a entreguei inteiramente pronta.

Você parece ter apanhado uma solução mais completa (mas também não tão completa) na Internet. Note que a ideia é semelhante à que eu lhe apresentei: um wrapper que captura o stream de saída ao qual é aplicado. A maior diferença entre o meu wrapper e o que você encontrou é que eu procuro devolver o stream original à expressão assim que possível (i.e. assim que um número fosse impresso), ao passo que o seu mantém o tipo de retorno como sendo o wrapper, mesmo que você imprima outros tipos de dados, e em várias operações sucessivas. Ele só restaura o stream envelopado à expressão quando você aplica um dos manipuladores da biblioteca padrão.

Ou não, porque, pelo que você falou, essa foi justamente a parte que deu erro.

Eu diria para você eliminar aquele “__cdecl”, principalmente se você não estiver usando Windows. E eu lamento que a Microsoft infeste seus sistemas com essas idiossincrasias de múltiplas convenções diferentes de passagem de argumento a funções, que é tão perniciosa que acaba vazando para os programas que nós fazemos, inclusive em algo que nem é tão complicado, como este caso que estamos analisando, ao lidar com um tipo de dados da própria biblioteca padrão (i.e. os ponteiros de função usados como tipo pelos manipuladores std::dec, std::hex e std::oct).

Alternativamente, você pode usar o preprocessador para tornar esse “__cdecl” (e seus congêneres) inócuo em sistemas não-Microsoft.

#if !defined(_WIN16) && !defined(_WIN32) && !defined(_WIN64)
#undef __cdecl
#undef __syscall
#undef __clrcall
#undef __thiscall
#undef __fastcall
#undef __vectorcall
// Que porre!
#define __cdecl
#define __syscall
#define __clrcall
#define __thiscall
#define __fastcall
#define __vectorcall
#endif


Com C++11, você pode fazer melhor ainda, e de um jeito que funcionará em qualquer sistema.

std::ostream &operator<<(decltype(std::dec) &_Pfn){
/* bla, bla, bla */
}



12. Re: Sobrecarregando operator de saida c++[RESOLVIDO]

Jean César
dark777

(usa Slackware)

Enviado em 15/12/2017 - 03:59h

paulo1205 escreveu:

dark777 escreveu:

Interessante porém estava pesquisando em outros forums e consegui chegar a este resultado


Minha resposta anterior foi no sentido de induzir você a uma solução. Ela, por si só, funcionava (embora eu tenha omitido especificamente a conversão para binário, em si), mas não a entreguei inteiramente pronta.

Você parece ter apanhado uma solução mais completa (mas também não tão completa) na Internet. Note que a ideia é semelhante à que eu lhe apresentei: um wrapper que captura o stream de saída ao qual é aplicado. A maior diferença entre o meu wrapper e o que você encontrou é que eu procuro devolver o stream original à expressão assim que possível (i.e. assim que um número fosse impresso), ao passo que o seu mantém o tipo de retorno como sendo o wrapper, mesmo que você imprima outros tipos de dados, e em várias operações sucessivas. Ele só restaura o stream envelopado à expressão quando você aplica um dos manipuladores da biblioteca padrão.

Ou não, porque, pelo que você falou, essa foi justamente a parte que deu erro.

Eu diria para você eliminar aquele “__cdecl”, principalmente se você não estiver usando Windows. E eu lamento que a Microsoft infeste seus sistemas com essas idiossincrasias de múltiplas convenções diferentes de passagem de argumento a funções, que é tão perniciosa que acaba vazando para os programas que nós fazemos, inclusive em algo que nem é tão complicado, como este caso que estamos analisando, ao lidar com um tipo de dados da própria biblioteca padrão (i.e. os ponteiros de função usados como tipo pelos manipuladores std::dec, std::hex e std::oct).

Alternativamente, você pode usar o preprocessador para tornar esse “__cdecl” (e seus congêneres) inócuo em sistemas não-Microsoft.

#if !defined(_WIN16) && !defined(_WIN32) && !defined(_WIN64)
#undef __cdecl
#undef __syscall
#undef __clrcall
#undef __thiscall
#undef __fastcall
#undef __vectorcall
// Que porre!
#define __cdecl
#define __syscall
#define __clrcall
#define __thiscall
#define __fastcall
#define __vectorcall
#endif


Com C++11, você pode fazer melhor ainda, e de um jeito que funcionará em qualquer sistema.

std::ostream &operator<<(decltype(std::dec) &_Pfn){
/* bla, bla, bla */
}


entendi dei uma pesquisada sobre esse __cdecl e nao tinha achado nada agora esta funcionando corretamente pois achei que era erro de sintaxe. modifiquei a parte do codigo adicionando sua sugestão e funcionou legal.. interessante...

wiki.anon