Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Klasa szablonowa zarządzana enum'em w zależności od typu kontenera

Ostatnio zmodyfikowano 2016-08-06 20:41
Autor Wiadomość
carlosmay
Temat założony przez niniejszego użytkownika
Klasa szablonowa zarządzana enum'em w zależności od typu kontenera
» 2016-05-22 00:08:50
Dla każdego kontenera osobno kod się kompiluje i działa prawidłowo.
Natomiast kod w całości nie chce działać. Wydaje mi się, że nieprawidłowo wykrywa Type,
gdy są oba kontenery. Nie wiem jak to ugryźć.
C/C++
#include <iostream>
#include <set>
#include <map>

class ComponentType
{
public:
    enum class eType { map, vector, set, simple };
};

template < typename Type >
class Show
{
    const ComponentType::eType mode;
public:
    Show( ComponentType::eType m )
        : mode( m )
    { }
   
    void FillComponent( Type & comp )
    {
        if( mode == ComponentType::eType::map )
        {
            comp.insert( { 2, 6 } );
            comp.insert( { 8, 6 } );
        }
        else if( mode == ComponentType::eType::set )
        {
            comp.insert( { 7 } );
            comp.insert( { 4 } );
        }
    }
   
    void PrintComponent( const Type & comp )
    {
        if( mode == ComponentType::eType::map )
        {
            for( const auto & item: comp )
            {
                std::cout << item.first << " : " << item.second << '\n';
            }
            std::cout << '\n';
        }
        else if( mode == ComponentType::eType::set )
        {
            for( const auto & item: comp )
            {
                std::cout << item << ' ';
            }
            std::cout << '\n';
        }
    }
   
};

int main()
{
    using Map = std::map < int, int, std::less < int >>;
    using Set = std::set < int, std::less < int >>;
   
    Map m;
    Set s;
   
    Show < Map > map( ComponentType::eType::map );
    map.FillComponent( m );
    Show < Set > set( ComponentType::eType::set );
    set.FillComponent( s );
   
    map.PrintComponent( m );
    set.PrintComponent( s );
}
P-148480
Monika90
» 2016-05-22 09:00:24
Nie da się tak zrobić. W C++ obydwie gałęzie if-else muszą zawierać poprawny kod. Nawet jeżeli wartość warunku if jest znana w czasie kompilacji.

Dlatego należy użyć przeciążonych funkcji
C/C++
#include <iostream>
#include <set>
#include <map>


void FillComponent( std::map < int, int > & comp )
{
    comp.insert( { 2, 6 } );
    comp.insert( { 8, 6 } );
}

void FillComponent( std::set < int > & comp )
{
    comp.insert( { 7 } );
    comp.insert( { 4 } );
}

template < class T >
void PrintItem( const T & item )
{
    std::cout << item << ' ';
}

template < class T, class U >
void PrintItem( const std::pair < T, U >& item )
{
    std::cout << item.first << " : " << item.second << '\n';
}

template < class Type >
void PrintComponent( const Type & comp )
{
    for( const auto & item: comp )
    {
        PrintItem( item );
    }
    std::cout << '\n';
}

int main()
{
    using Map = std::map < int, int, std::less < int >>;
    using Set = std::set < int, std::less < int >>;
   
    Map m;
    Set s;
   
    FillComponent( m );
    FillComponent( s );
   
    PrintComponent( m );
    PrintComponent( s );
}

P-148487
Monika90
» 2016-05-22 10:23:29
Można też zrobić to tak
C/C++
#include <iostream>
#include <set>
#include <map>

template < bool cond >
struct static_if;

template <>
struct static_if < true >
{
    template < class Func1, class Func2 >
    static auto get( Func1 f1, Func2 ) { return f1; }
};

template <>
struct static_if < false >
{
    template < class Func1, class Func2 >
    static auto get( Func1, Func2 f2 ) { return f2; }
};

class ComponentType
{
public:
    enum class eType { map, vector, set, simple };
};

template < typename Type, ComponentType::eType mode >
class Show
{
public:
    void FillComponent( Type & comp )
    {
        static_if < mode == ComponentType::eType::map >::get(
        []( auto & comp ) {
            comp.insert( { 2, 6 } );
            comp.insert( { 8, 6 } );
        },
        []( auto & comp ) {
            comp.insert( { 7 } );
            comp.insert( { 4 } );
        } )( comp );
    }
   
    void PrintComponent( const Type & comp )
    {
        static_if < mode == ComponentType::eType::map >::get(
        []( auto & comp ) {
            for( const auto & item: comp )
            {
                std::cout << item.first << " : " << item.second << '\n';
            }
            std::cout << '\n';
        },
        []( auto & comp ) {
            for( const auto & item: comp )
            {
                std::cout << item << ' ';
            }
            std::cout << '\n';
        } )( comp );
    }
   
};

int main()
{
    using Map = std::map < int, int, std::less < int >>;
    using Set = std::set < int, std::less < int >>;
   
    Map m;
    Set s;
   
    Show < Map, ComponentType::eType::map > map;
    map.FillComponent( m );
    Show < Set, ComponentType::eType::set > set;
    set.FillComponent( s );
   
    map.PrintComponent( m );
    set.PrintComponent( s );
}
Ale sam widzisz, że to bez sensu.
P-148488
carlosmay
Temat założony przez niniejszego użytkownika
» 2016-05-22 14:15:01
W C++ obydwie gałęzie if-else muszą zawierać poprawny kod.
Już rozumiem.

Piszę kod cały obiektowy. Nie chciałem pisać osobnej klasy dla każdego kontenera o odmiennym sposobie (wypisywania) wstawiania elementów.
To jest tylko próbna klasa (nazwy nie adekwatne do funkcjonalności). Miło by było dla oka ustawiać enum'em.

Dzięki za wskazówki.
P-148496
carlosmay
Temat założony przez niniejszego użytkownika
» 2016-08-06 02:35:00
Dzięki za pomoc. Rozwiązałem to w ten sposób. Może ktoś skorzysta.
C/C++
#include <iostream>
#include <type_traits>
#include <set>
#include <map>
#include <vector>
#include <string>

struct UnknownType { };
struct InsertType { };
struct PushBackType { };

template < class Type >
struct InserterType { using type = UnknownType; };

template < class Type, class Compare, class Alloc >
struct InserterType < std::set < Type, Compare, Alloc >> { using type = InsertType; };

template < class Key, class Value, class Compare, class Alloc >
struct InserterType < std::map < Key, Value, Compare, Alloc >> { using type = InsertType; };

template < class Type, class Alloc >
struct InserterType < std::vector < Type, Alloc >> { using type = PushBackType; };

template < class Container, class ValueType >
void myInsert( InsertType, Container & coll, ValueType && value ) {
    coll.insert( std::forward < ValueType >( value ) );
}

template < class Container, class ValueType >
void myInsert( PushBackType, Container & coll, ValueType && value ) {
    coll.push_back( std::forward < ValueType >( value ) );
}

template < class Container >
void myInsert( Container & coll, typename std::decay_t < Container >::value_type value ) {
    using DeducedInserterCollectionType = typename InserterType < std::decay_t < Container >>::type;
    myInsert( DeducedInserterCollectionType { }, coll, std::move( value ) );
}

int main()
{
    std::map < int, std::string > mis = { { 1, " pierwszy wpis" } };
    std::set < int > si = { 1, 2, 3 };
    std::vector < int > vi { 4, 5, 6 };
   
    myInsert( mis, { 2, " drugi wpis" } );
    myInsert( mis, { 3, " trzeci wpis" } );
    myInsert( si, 11 );
    myInsert( si, 13 );
    myInsert( vi, 44 );
    myInsert( vi, 55 );
   
    for( auto const & element: mis ) {
        std::cout << element.first << element.second << '\n';
    }
   
    for( auto const & element: si ) {
        std::cout << element << ' ';
    }
    std::cout << '\n';
   
    for( auto const & element: vi ) {
        std::cout << element << ' ';
    }
    std::cout << '\n';
}
P-150593
michal11
» 2016-08-06 12:11:25
Jak rozumiem chciałeś zrobić uniwersalną funkcję do wrzucania elementów do kontenera, jestem na telefonie wiec nie mogę tego dokładnie sprawdzić, ale czy nie mogłeś użyć do tego iteratorów ?

Edit.
Ja zrobiłem coś takiego:

C/C++
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <iterator>

template < class Container >
void myInsert( Container & x, typename Container::value_type && value )
{
    x.insert( std::end( x ), value );
}


int main()
{
    std::map < int, std::string > mis = { { 1, " pierwszy wpis" } };
    std::set < int > si = { 1, 2, 3 };
    std::vector < int > vi { 4, 5, 6 };
   
    myInsert( mis, { 2, " drugi wpis" } );
    myInsert( mis, { 3, " trzeci wpis" } );
    myInsert( si, 11 );
    myInsert( si, 13 );
    myInsert( vi, 44 );
    myInsert( vi, 55 );
   
   
    for( auto const & element: mis ) {
        std::cout << element.first << element.second << '\n';
    }
   
    for( auto const & element: si ) {
        std::cout << element << ' ';
    }
    std::cout << '\n';
   
    for( auto const & element: vi ) {
        std::cout << element << ' ';
    }
    std::cout << '\n';
   
    return 0;
}

Niestety nie mogę porównać outputu bo twój kod mi się nie kompiluje (gcc version 5.3.1 na http://www.tutorialspoint.com​/codingground.htm) ale wątpię, żeby nie działało tak jak chcesz.
P-150594
carlosmay
Temat założony przez niniejszego użytkownika
» 2016-08-06 20:41:22
Niestety nie mogę porównać outputu bo twój kod mi się nie kompiluje
Piszę w VS, a on rządzi się swoimi prawami.

Przyznam, że nie wpadłem na takie rozwiązanie.
Obecnie, bardziej przyda mi się moje rozwiązanie.
Twoje rozwiązanie michal11 warto zapamiętać.

edit: zrobiłem test zapisu do kontenerów i użycie struktur daje czas trzy razy krótszy niż użycie iteratora.
W C::B na gcc 5.1.0 różnica jest mniejsza, ale nadal na korzyść struktur.

C/C++
int main()
{
    constexpr std::size_t nLoops = 10000000;
    using TimePoint = std::chrono::high_resolution_clock::time_point;
   
    std::map < int, std::string > mis = { { 1, " pierwszy wpis" } };
    std::set < int > si = { 1, 2, 3 };
    std::vector < int > vi { 4, 5, 6 };
   
    TimePoint start = std::chrono::high_resolution_clock::now();
    for( std::size_t i = 0; i < nLoops; ++i ) {
        myInsert( mis, { 2, " drugi wpis" } );
        myInsert( mis, { 3, " trzeci wpis" } );
        myInsert( si, 11 );
        myInsert( si, 13 );
        myInsert( vi, 44 );
        myInsert( vi, 55 );
    }
    TimePoint stop = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast < std::chrono::duration < double >>( stop - start ).count() << " s" << '\n';
}

vs: 0,7s struct - 2,4s iterator
gcc: 7,3s struct - 9,3s iterator
P-150605
« 1 »
  Strona 1 z 1