Boost::unordered_map C++ comportamento estranho

Oi pessoal,

Estou usando um boost::unordered_map <const char *,std::vector ><const char *> > para armazenar dados de um arquivo.

Meu problema é que a comparação entre chaves não parece estar funcionando como esperado…

Por exemplo, no arquivo tenho uma chave "tam_bloco" que é armazenada corretamente no mapa e seu vector associado também é adicionado sem problema.

Mas quando dou um get ("tam_bloco") nesse mapa, ele diz que não encontrou nada no mapa com essa chave…embora antes disso eu ter acabado de verificar
que a chave está sim no mapa, e com um vector com alguns elementos associado…

Pesquisando um pouco, descobri que poderia ser necessário informar uma função para o mapa usar para comparar as chaves…então passei a usar um

boost::unordered_map <const char *,std::vector ><const char *>, boost::hash<const char *>,eqstr >

Esse eqstr é isso aqui:

[code]struct eqstr
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) == 0;
}

};[/code]

O bizarro é que com esse código, executo várias vezes e obtenho resultados diferentes…às vezes ele acha a chave no mapa…e às vezes diz que não tem nada…o mesmíssimo código…sem alterar nada…mostra resultados diferentes a cada execução…como pode isso?

Cheguei a tentar usar uma std::equal_to<const char *> nesse mesmo mapa, no lugar de eqstr…mas aí ele nunca encontra a chave que tenho certeza que está lá…alguma luz?

"Resolvi" o problema trocando minha função de comparação para

bool operator()(std::string s1, std::string s2) const
    {


  	  return s1.compare(s2) == 0;


    }

Mas gostaria muito se alguém comentasse o problema que mostrei acima.

Rodrigo.

Se é um mapa de Strings, por que não fez um std::map<string, string>?

Não use char*. Além de ser bem mais difícil e sujeito a erros, é mais lento que usar a classe std::string.

Não se esqueça também que em C++ strings são objetos mutáveis. E você não deve alterar seu conteúdo uma vez que elas se tornem chaves no mapa.

[quote=ViniGodoy]Se é um mapa de Strings, por que não fez um std::map<string, string>?

Não use char*. Além de ser bem mais difícil e sujeito a erros, é mais lento que usar a classe std::string.

Não se esqueça também que em C++ strings são objetos mutáveis. E você não deve alterar seu conteúdo uma vez que elas se tornem chaves no mapa.[/quote]

Pra ser sincero nem lembro mais pq to usando char *. Acho que é pq estou usando coisas do C, como strtok. Aliás, tem algum método split em c++?

No Boost, quando você usa o unordered_map como

typedef unordered_map<char *, char *> map_charptr;

ou coisa parecida, na verdade ele está usando o default

typedef unordered_map<char *, char *, boost::hash<char *> > map_charptr;

Se você olhar a definição de boost::hash (em boost/functional/hash.hpp), vai ver que existe uma especialização desse template para boost::hash mas não para char*. Você poderia fazer algo como:

#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>
#include <boost/functional/hash.hpp>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
using namespace boost;

struct char_hasher {
  // faz o cálculo do hash, usando hash_combine<string>
  std::size_t operator()(char * const& p) const {
      std::size_t hs = 0;
		// não use boost::hash_combine (hs, p); porque ele usa o valor do ponteiro, não interpreta como uma string.
		// A forma mais simples de trabalhar com isso seria:
	  boost::hash_combine (hs, string(p)); // ok, você pode criar seu próprio método de hash
	  cout << p << "==>" << hex << setw(8) << setfill ('0') << hs << endl;
	  return hs;
  }
  // implementa std::equal_to comparando os conteúdos, não os ponteiros
  bool operator()(const char * x, const char * y) const {
	bool ret = strcmp (x, y) == 0;
	cout << x << (ret ? "==" : "!=") << y << endl;
	return ret;
  }
};
typedef unordered_map<char*, char*, char_hasher, char_hasher > map_char;

int main (int argc, char *argv[]) {
    map_char m;
	m ["abc"] = "def";
	m ["pqs"] = "xyz";
	cout << "--- Listando o mapa ---" << endl;
	BOOST_FOREACH (map_char::value_type p, m) {
	    cout << p.first << "," << p.second << endl;
	}
	cout << "--- Acessando o mapa ---" << endl;
	cout << m["abc"] << endl;
	if (m.find("qqq") != m.end()) {
		cout << m["qqq"] << endl;
	} // note que se não testar antes de referenciar, vai provocar uma falha de memória. 
}

[quote=rodrigo.bossini][quote=ViniGodoy]Se é um mapa de Strings, por que não fez um std::map<string, string>?

Não use char*. Além de ser bem mais difícil e sujeito a erros, é mais lento que usar a classe std::string.

Não se esqueça também que em C++ strings são objetos mutáveis. E você não deve alterar seu conteúdo uma vez que elas se tornem chaves no mapa.[/quote]

Pra ser sincero nem lembro mais pq to usando char *. Acho que é pq estou usando coisas do C, como strtok. Aliás, tem algum método split em c++?[/quote]

Se estiver usando o Boost, use boost::regex_split (ou então regex_token_iterator) , se quiser fazer uma cópia “ipsis litteris” do String.split do Java, ou então boost::algorithm::string::split (que é mais leve que o regex_split) se a string for fixa ou então for um de uma série de caracteres.

Em particular, prefiro usar um unordered_map<string, string> porque se você tiver algo como:

typedef unordered_map<string, string> map_string;

map_string m;

m["abc"] = "def";
cout << m["def"] << endl;

em vez de quebrar seu programa ao não conseguir acessar uma chave inexistente, vai retornar uma string vazia, o que normalmente é uma coisa desejável (embora tenha o efeito colateral de fazer o equivalente a m[“def”] = “” - :frowning: )

[quote=entanglement][quote=rodrigo.bossini][quote=ViniGodoy]Se é um mapa de Strings, por que não fez um std::map<string, string>?

Não use char*. Além de ser bem mais difícil e sujeito a erros, é mais lento que usar a classe std::string.

Não se esqueça também que em C++ strings são objetos mutáveis. E você não deve alterar seu conteúdo uma vez que elas se tornem chaves no mapa.[/quote]

Pra ser sincero nem lembro mais pq to usando char *. Acho que é pq estou usando coisas do C, como strtok. Aliás, tem algum método split em c++?[/quote]

Se estiver usando o Boost, use boost::regex_split (ou então regex_token_iterator) , se quiser fazer uma cópia “ipsis litteris” do String.split do Java, ou então boost::algorithm::string::split (que é mais leve que o regex_split) se a string for fixa ou então for um de uma série de caracteres. [/quote]

To tentando usar o split da boost e tá dando erro…tem alguma opção que tenho de passar para o linker?

#include <boost/algorithm/string/split.hpp> // split
#include <boost/algorithm/string/classification.hpp> // is_any_of
#include <boost/foreach.hpp> // BOOST_FOREACH
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main (int argc, char *argv[]) {
    string st = "cafe;soja.trigo;milho-acucar;algodao,laranja";
	vector<string> mercadorias;
	boost::split (mercadorias, st, boost::algorithm::is_any_of (";,.-"));
	BOOST_FOREACH (string mercadoria, mercadorias) {
	    cout << mercadoria << endl;
	}
}

Uai - compilei o programa acima (tanto com o Microsoft C++ quanto com o g++) e não tive problemas. Por acaso você está usando alguma opção esquisita do Boost que força o uso de bibliotecas dinâmicas (DLL ou .SO) ?

[quote=rodrigo.bossini][quote=entanglement][quote=rodrigo.bossini][quote=ViniGodoy]Se é um mapa de Strings, por que não fez um std::map<string, string>?

Não use char*. Além de ser bem mais difícil e sujeito a erros, é mais lento que usar a classe std::string.

Não se esqueça também que em C++ strings são objetos mutáveis. E você não deve alterar seu conteúdo uma vez que elas se tornem chaves no mapa.[/quote]

Pra ser sincero nem lembro mais pq to usando char *. Acho que é pq estou usando coisas do C, como strtok. Aliás, tem algum método split em c++?[/quote]

Se estiver usando o Boost, use boost::regex_split (ou então regex_token_iterator) , se quiser fazer uma cópia “ipsis litteris” do String.split do Java, ou então boost::algorithm::string::split (que é mais leve que o regex_split) se a string for fixa ou então for um de uma série de caracteres. [/quote]

To tentando usar o split da boost e tá dando erro…tem alguma opção que tenho de passar para o linker?[/quote]

Não se esqueça de incluir os headers das classes. O compilador não irá te avisar que estão "faltando " explicitamente.
#include <boost/algorithm/string.hpp>

por via das dúvidas consulte a documentação.

http://www.boost.org/doc/libs/1_48_0/doc/html/string_algo.html
http://www.boost.org/doc/libs/1_48_0/doc/html/string_algo/usage.html#id3115768

muito interessante esses assuntos, parece bem complexo e desafiante

vou dar uma aproveitada =)

conheço C e um pouco sobre ponteiros, filas, pilhas…etc…

vocês poderiam me indicar algum material pra partir pro C++ e essa tal de boost ??? que não seja tão dificil…

acabei de dar uma lida nesse site: http://pt.kioskea.net/faq/10156-introducao-ao-stl-em-c-standard-template-library

nao sei se tem muito a ver…

muito obrigado!

[quote=douglaskd]muito interessante esses assuntos, parece bem complexo e desafiante

vou dar uma aproveitada =)

conheço C e um pouco sobre ponteiros, filas, pilhas…etc…

vocês poderiam me indicar algum material pra partir pro C++ e essa tal de boost ??? que não seja tão dificil…

acabei de dar uma lida nesse site: http://pt.kioskea.net/faq/10156-introducao-ao-stl-em-c-standard-template-library

nao sei se tem muito a ver…

muito obrigado![/quote]

Eu acho a boost muito leve e interressante, mas acho também o qt framework mais robusto. Ele possui todas esses containers e estruturass implementados usando memory sharing.
http://qt.nokia.com/products/
http://qt.nokia.com/learning

Eu poderia resolver o problema acima com o método QString::split

http://doc.qt.nokia.com/latest/qstring.html
http://doc.qt.nokia.com/latest/qstring.html#split

http://doc.qt.nokia.com/latest/qsharedmemory.html#details

só mais algumas dúvidas…

o QT é um framework grafico certo?, eu conseguiria trabalhar com estrutuas nele com facilidade sem conhecer a Boost…?

ja a boost é um framework parecido com o QT, ex…possui modo gráfico essas coisas?

dentre as duas, qual seria mais interessante aprender, se meu interesse fosse sistemas embarcados…

Obrigado

  1. Você pode usar a biblioteca Qt sem ter uma aplicação “gráfica”, usando apenas as estruturas de dados e outras facilidades (como suporte a threading) que ela tem.
  2. A biblioteca Boost tem uma orientação completamente diferente: ela não é para gráficos, mas para todas as outras coisas (suporte a threading, estruturas de dados, grafos, algoritmos diversos, etc.)

[quote=entanglement]1) Você pode usar a biblioteca Qt sem ter uma aplicação “gráfica”, usando apenas as estruturas de dados e outras facilidades (como suporte a threading) que ela tem.
2) A biblioteca Boost tem uma orientação completamente diferente: ela não é para gráficos, mas para todas as outras coisas (suporte a threading, estruturas de dados, grafos, algoritmos diversos, etc.)
[/quote]

Sim, e como a boost se torna parte da standard lib no c++11, você ganrante a portabilidade entre plataformas.

[quote=douglaskd]só mais algumas dúvidas…

o QT é um framework grafico certo?, eu conseguiria trabalhar com estrutuas nele com facilidade sem conhecer a Boost…?

ja a boost é um framework parecido com o QT, ex…possui modo gráfico essas coisas?

dentre as duas, qual seria mais interessante aprender, se meu interesse fosse sistemas embarcados…

Obrigado[/quote]

Para embarcados qualquer uma das duas.

No caso qt é um “framework” de alto nível para você trabalhar com databases, imagens, threads, documentos, sistema de arquivos, gráficos acelerados, networking, etc…

A boost é uma “biblioteca” que te garante uma enorme facilidade para system programming.

É como o entanglement falou. Se você quer software exuto para rodar em hardware de baixo custo, a boost é mais indicada.

[quote=entanglement][code]
#include <boost/algorithm/string/split.hpp> // split
#include <boost/algorithm/string/classification.hpp> // is_any_of
#include <boost/foreach.hpp> // BOOST_FOREACH
#include
#include
#include

using namespace std;

int main (int argc, char *argv[]) {
string st = “cafe;soja.trigo;milho-acucar;algodao,laranja”;
vector mercadorias;
boost::split (mercadorias, st, boost::algorithm::is_any_of (";,.-"));
BOOST_FOREACH (string mercadoria, mercadorias) {
cout << mercadoria << endl;
}
}
[/code]

Uai - compilei o programa acima (tanto com o Microsoft C++ quanto com o g++) e não tive problemas. Por acaso você está usando alguma opção esquisita do Boost que força o uso de bibliotecas dinâmicas (DLL ou .SO) ?
[/quote]

Acabei descobrindo que se trata de um bug do CDT. Bastou desativar o “code analysis” no eclipse pra (obviamente) o erro não ser mais informado. E o g++ compila tudo normalmente. Ele me mostrava erro quando tentava usar o foreach da boost também.