2012/2/13 Thiago Adams <thiago...@gmail.com>:
> --
> Antes de enviar um e-mail para o grupo leia:
> http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
> --~--~---------~--~----~---------------------------------~----------~--~----~
> [&] Colabore com a Pesquisa de Preferência de Conteúdo
> para Eventos do Grupo C & C++ Brasil:
> http://www.surveymonkey.com/s/GBBGTXN
> ------~----~-------~---~---~---~---~----------------~------------~---------~
> [&] C & C++ Brasil - http://www.ccppbrasil.org/
> Para sair dessa lista, envie um e-mail para ccppbrasil-...@googlegroups.com
> Para mais opções, visite http://groups.google.com/group/ccppbrasil
> --~--~---------~--~----~--~-~--~---~----~-----------------~--~----------~
> Emprego & carreira: vag...@ccppbrasil.org
> http://groups.google.com/group/dev-guys?hl=en
--
Felipe Magno de Almeida
"Numa API genérica você normalmente *não* quer "habilitar" uma function"
Faltou o não.
2012/2/15 Felipe Magno de Almeida <felipe.m...@gmail.com>:
> Eu faço bastante programas com programação genérica, e posso te dizer
> que concepts seria um plus absurdo em termos de especificação da API.
> Enable_if é legal, mas é só um hack geralmente. Numa API genérica
> você normalmente *não* quer "habilitar" uma function template se X, você quer
Poder usar, pode, posso inclusive resolver com enable_if e/ou traits.
Mas não resolve a API de verdade. Uma API genérica possui
conceitos e tipos que modelam esses conceitos. Como eu tenho que
fazer hoje? Eu documento o conceito (o próprio padrão faz o mesmo
com iteradores e containers por exemplo), crio traits que definem
as metafunções para acesso de meta-dados (geralmente os tipos
relacionados ao conceito). Documento como cada metafunção deve
ser estendida (specialization, overload ou sei lá como), e uso essas
meta-funções nos algoritmos. Protejo os algoritmos através de
alguma meta-função como
does_this_really_models_the_concept_I_want<T>::type::value.
Só que o problema é que nada desse código tem a ver de fato com
o conceito. Os requerimentos do conceito deveriam ser localizados
juntos (num concept), e os algoritmos não deveriam impor os
requerimentos aos seus parametros, e sim definir quais parâmetros
ele recebe e usar essa checagem dos concepts.
Existe uma diferença bem grande de:
template <RandomAccessIterator I>
I find(I first, I last, RandomAccessIterator<I>::value_type o);
E dizer:
template <typename I>
I find_ra(I first, I last, typename std::iterator_traits<I>::value_type o);
template <typename I>
I find(I first, I last, typename std::iterator_traits<I>::value_type o)
{
static if (boost::is_convertible<typename
std::iterator_traits<I>::tag,
std::random_access_iterator_tag>::type::value)
{
return find_ra(first, last, o);
}
else // provavelmente escrever na mao a sobrecarga
;
}
Na verdade, muito melhor que static if nesse caso é simplesmente usar
tags como já é feito há muito tempo. Mas
o problema aí que eu quero dizer é que os requerimentos estão nos
algoritmos (is_convertible, iterator_traits<I>::tag)
e não na definição do conceito. Por exemplo, um iterador que faça
especialização de iterator_traits e defina apenas
tag e value_type irá funcionar nesse algoritmo, mesmo que ele não seja
de fato um RandomAccessIterator.
É por isso que em C++03 (que eu uso exclusivamente ainda), eu uso
boost.concept_check. Assim eu defino o conceito:
template <typename X>
struct FooConcept
{
typedef foo_traits<X> traits;
typedef typename traits::value_type value_type;
typedef typename traits::something_type something_type;
typedef typename traits::iterator_type iterator_type;
typedef typename traits::is_foo is_foo;
BOOST_MPL_ASSERT_RELATION(is_foo::value, ==, true); // igual a
numeric_limits<X>::is_specialized
BOOST_CONCEPT_ASSERT((boost::ForwardIteratorConcept<iterator_type>));
// iterator_type in traits must be at least
// a forward iterator
BOOST_CONCEPT_USAGE(FooConcept)
{
is_convertible_to<traits::value_type>(traits::value(x));
}
template <typename To, typename From>
void is_convertible_to(From const&)
{
BOOST_MPL_ASSERT((boost::is_convertible<From, To>));
}
X x;
};
No algoritmo então eu digo:
template <typename F>
void do_something_with_foo(F f)
{
BOOST_CONCEPT_ASSERT((FooConcept<F>)); // F realmente modela FooConcept !
}
E se eu for fazer um refinement de Foo, então eu preciso criar as tags
e criar as hierarquias de tags.
Acho que dá para ver como é "capenga" esse modelo comparado com uso de concepts.
> --
A do Stroustoup realmente indica que "concepts"
é um conceito muito abstrato e complexo, pelo
menos na generalidade com que eles querem
fazer as coisas.
O que eu ganho é que a definição do conceito é feita em um único
ponto, e o próprio código
é auto-documentável. Assim eu não preciso verificar que cada algoritmo
de fato verifica
todos os requerimentos corretamente, basta ele ter um BOOST_CONCEPT_ASSERT
daquele conceito e daí eu sei que: se o conceito estiver bem definido
em código e a
instanciação do algoritmo ocorreu sem erros, então está OK; ou o
conceito está mal
escrito e precisa ser consertado.
Também possibilita a definição de arquétipos baseado apenas no
conceito para teste
sintático dos algoritmos. Os concepts em linguagem trazem ainda mais
uma vantagem
que é a checagem automática dos algoritmos nessa última parte (AFAIK), já que
qualquer uso fora do concept no algoritmo gera um erro (novamente AFAIK).
> E quando você diz:
>
> // F realmente modela FooConcept !
> BOOST_CONCEPT_ASSERT((FooConcept<F>));
>
> O que é o "realmente"? É semantica?
É tudo que é checável do conceito. Assume-se aí que se ele definiu
foo_traits<T>::is_foo
como true, então T modela semanticamente também FooConcept. É o equivalente do
não-auto-concept (não sei como chamam atualmente).
[snip]
> Só acho estranho falar sobre meta tipos e tentar capturar
> a semantica por trás deles enquanto a linguagem não
> te dá, por exemplo, a lista de classes das quais
> uma classe deriva imediatamente.
Nunca precisei disso. Num concept eu defino os requerimentos,
por exemplo quais bases precisa herdar se necessário etc. Não
preciso inspecionar o tipo nesse nível de detalhe fazendo
programação genérica, só preciso testar que o tipo passado de fato
obedece aos requerimentos. Programação genérica != metaprogramação
AFAIK. Apesar de os dois poderem se misturar, os dois podem
também conviver muito bem sozinhos, usar templates não
significa fazer metaprogramas.
> Bom, são só pensamentos desorganizados. Continuem
> com a conversa, está interessante.
>
> walter.
[]'s
E a parte da semantica não é verificada, só é descoberto se
der erro em runtime. Por exemplo, se eu mentir que um retangulo
é um quadrado e ele assumir que são lados iguais pode dar problema
na hora de calcular.
Ainda existe uma parte dos axiomas na proposta dos concepts mas
nao entendi aonde seria usado.
No slide 32 diz:
Axiom – a semantic requirement on a type, an invariant that cannot be
evaluated at compile time;
they are assumed
–like a precondition
Só acho estranho falar sobre meta tipos e tentar capturar
a semantica por trás deles enquanto a linguagem não
te dá, por exemplo, a lista de classes das quais
uma classe deriva imediatamente.