Podział na plik nagłówkowy i plik z rozwinięciem klas

0

Witam szanowną społeczność :)

Nie wiem, co robię źle. Zamieszczam trzy kody źródłowe:

Graph.hpp

 
#ifndef GRAPH_HPP_INCLUDED
#define GRAPH_HPP_INCLUDED

#include <iostream>
#include <utility>
#include <vector>

#include "Graph.cpp" //??

using namespace std;

template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
struct VertexEdge
{
    Vertex<Value> *first, *second;
    Weight *weight;
};



template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
class Graph
{
   private:

     //Exceptions classes

     class NullVertexException{};
     class NullWeightException{};
     class NullEdgesException{};
     class VertexIndexOfBoundsException{};


     struct Edge //internal
     {
         Vertex<Value> *first, *second;
         Weight *weight;
     };


     vector< Vertex<Value>* >  *Vertexes;
     vector< Edge* >            Edges;

     bool RaiseExceptions;

  public:

     Graph(bool Exceptions);

     unsigned int AddVertex(Vertex<Value> *vertex);

     bool AddEdge(Vertex<Value> *first, Vertex<Value> *second, Weight *weight);

     bool AddEdge(unsigned int index1, unsigned int index2, Weight *weight);

     Vertex<Value>*  Get(unsigned int index);

     Weight*  GetWeight(Vertex<Value> *first, Vertex<Value> *second);

     Weight*  GetWeight(unsigned int index1, unsigned int index2);

     pair< Vertex<Value>*, Vertex<Value>* >*  GetMaxWeightPair();

     Weight*  GetMaxWeight();

     vector< Vertex<Value>* >*   GetVertexes();

     vector< VertexEdge<Value, Weight, Vertex>* >*   GetEdges();
};

#endif // GRAPH_HPP_INCLUDED

Graph.cpp:

 
#include <iostream>
#include <utility>
#include <vector>

#include "Graph.hpp"

using namespace std;


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex

Graph<Value, Weight, Vertex > :: Graph(bool Exceptions = false) : RaiseExceptions(Exceptions)
{
    Vertexes = new vector< Vertex<Value>* >;
}



template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
unsigned int Graph<Value, Weight, Vertex> :: AddVertex(Vertex<Value> *vertex)
{
    if (vertex == NULL)
    {
        if (RaiseExceptions==true)
            throw new NullVertexException;
        else
            return -1;
    }
    Vertexes->push_back(vertex);

    return Vertexes->size();
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
bool Graph<Value, Weight, Vertex> :: AddEdge(Vertex<Value> *first, Vertex<Value> *second, Weight *weight)
{
    if (first == NULL || second == NULL)
    {
        if (RaiseExceptions==true)
            throw new NullVertexException;
        else
            return false;
    }

    if (weight == NULL)
    {
        if (RaiseExceptions==true)
            throw new NullWeightException;
        else
            return false;
    }

    Edge *edge = new Edge;

    edge->first  = first;
    edge->second = second;
    edge->weight = weight;

    Edges.push_back( edge );
    return true;
}

template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
bool Graph<Value, Weight, Vertex> :: AddEdge(unsigned int index1, unsigned int index2, Weight *weight)
{
    return AddEdge(Get(index1), Get(index2), weight);
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
Vertex<Value>*  Graph<Value, Weight, Vertex> ::  Get(unsigned int index)
{
    if (index > Vertexes.size())
    {
        if (RaiseExceptions == true)
            throw new VertexIndexOfBoundsException;
        else
            return NULL;
    }
    else
        return Vertexes[index];
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
Weight*  Graph<Value, Weight, Vertex> :: GetWeight(Vertex<Value> *first, Vertex<Value> *second)
{
    int size = Edges.size(), count = 0;

    if (size==0) return NULL;

    for (int i=0;  i<size;  ++i)
        if (Edges[i].first == first && Edges[i].second == second)
            return Edges[i].weight;

    return NULL;
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
Weight*  Graph<Value, Weight, Vertex> :: GetWeight(unsigned int index1, unsigned int index2)
{
    return GetWeight(Get(index1), Get(index2));
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
pair< Vertex<Value>*, Vertex<Value>* >*  Graph<Value, Weight, Vertex> :: GetMaxWeightPair()
{
    if (Edges.size() == 0)
    {
        if (RaiseExceptions == true)
            throw new NullEdgesException;
        else
            return NULL;
    }

    Weight *max = Edges[0]->weight;
    int index = 1;

    for (int i=1;  i<Edges.size();  ++i)
    {
        if (Edges[i]->weight > max)
        {
            max = Edges[i]->weight;
            index = i;
        }
    }

    return new pair< Vertex<Value>*, Vertex<Value>* >
                     (Edges[index]->first, Edges[index]->second);
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
Weight*  Graph<Value, Weight, Vertex> :: GetMaxWeight()
{
    if (Edges.size() == 0)
    {
        if (RaiseExceptions == true)
            throw new NullEdgesException;
        else
            return NULL;
    }

    Weight *max = Edges[0]->weight;
    int index = 1;

    for (int i=1;  i<Edges.size();  ++i)
    {
         if (Edges[i]->weight > max)
         {
             max = Edges[i]->weight;
             index = i;
         }
    }
    return Edges[index]->weight;
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
vector< Vertex<Value>* >*  Graph<Value, Weight, Vertex> ::  GetVertexes()
{
     return Vertexes;
}


template
<
   typename Value,
   typename Weight,

   template <class> class Vertex
>
vector< VertexEdge<Value, Weight, Vertex>* >*   Graph<Value, Weight, Vertex> ::  GetEdges()
{
     if (Edges.size() == 0 )
     {
         if (RaiseExceptions == true)
             throw new NullEdgesException;
         else
             return NULL;
     }

     vector< VertexEdge<Value, Weight, Vertex>* >  *vec =
     new vector< VertexEdge<Value, Weight, Vertex>* >;

     for (int i=0;  i<Edges.size();  ++i)
     {
         VertexEdge<Value, Weight, Vertex> *ve = new VertexEdge<Value, Weight, Vertex>;

         ve->first  = Edges[i]->first;
         ve->second = Edges[i]->second;
         ve->weight = Edges[i]->weight;

         vec->push_back(ve);
     }
     return vec;
}

main.cpp:

 
#include "Graph.hpp"
#include <iostream>

using namespace std;


template <typename Value>
class Vertex
{
      Value *obj;

   public:

      Vertex(Value *value) : obj(value){}

      Value*  Get(){ return obj; }
};


class Weight
{
    int *obj;

  public:

    Weight(int *weight) : obj(weight){}

    int*  Get(){ return obj; }
};


int main()
{
    Graph<int, Weight, Vertex>  graph(true);

    for (int i=0;  i<10;  ++i)
    {
        Vertex<int>
        *v1 = new Vertex<int>( new int (i) ),
        *v2 = new Vertex<int>( new int (i*2));
        graph.AddVertex(v1);
        graph.AddVertex(v2);
        graph.AddEdge(v1,v2, new Weight(new int(i*i-2*i)));
    }

    //pair< Vertex<int>*, Vertex<int>* >  *p  =  graph.GetMaxWeight();

    //cout << *(p->first->Get() );

    cout << *(graph.GetMaxWeight()->Get());

}

Błędy wyrzucone przez kompilator (Code::Blocks + GCC)

/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: ‘Graph’ does not name a type|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|31|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|53|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|88|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|101|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|122|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|143|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|156|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|190|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|222|error: expected initializer before ‘<’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|235|error: ‘VertexEdge’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|235|error: wrong number of template arguments (3, should be 2)|
/usr/include/c++/4.6/bits/stl_vector.h|180|error: provided for ‘template<class _Tp, class _Alloc> class std::vector’|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|235|error: expected unqualified-id before ‘>’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|235|error: expected initializer before ‘>’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: ‘Value’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: ‘Weight’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: ‘Vertex’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: template argument 1 is invalid|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: template argument 2 is invalid|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: template argument 3 is invalid|
/home/cx3/Pulpit/CB/Graph/Graph.cpp||In function ‘int Graph(bool)’:|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: ‘int Graph(bool)’ redeclared as different kind of symbol|
/home/cx3/Pulpit/CB/Graph/Graph.hpp|34|error: previous declaration of ‘template<class Value, class Weight, template<class> class Vertex> class Graph’|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|17|error: only constructors take member initializers|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: ‘Vertexes’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: ‘Vertex’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: ‘Value’ was not declared in this scope|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: template argument 1 is invalid|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: template argument 2 is invalid|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|19|error: expected primary-expression before ‘;’ token|
/home/cx3/Pulpit/CB/Graph/Graph.cpp|20|warning: no return statement in function returning non-void [-Wreturn-type]|
||=== Build finished: 30 errors, 1 warnings ===|

Dopóki wszystkie klasy były w jednym pliku main.cpp wszystko działało.
Bardzo proszę o pomoc i z góry dziękuję :)

Pozdro,
cx3

2

Ciała szablonów muszą znajdować się w nagłówku.

2

nie domknąłeś parametrów szablonu w Graph.cpp. Brakuje ">" po template <class> class Vertex.

0
#include "Graph.cpp" //??

Wywal to. Zrób jak @spartanPAGE napisał.

0

Domknąłem token > . Usunąłem wskazany #include. Nie wiem co robię nie tak jak należy, jeśli chodzi o programowanie generyczne stawiam pierwsze kroki. Kompilator wyrzuca następujące błędy:

obj/Debug/main.o||In function main':| /home/cx3/Pulpit/CB/Graph/main.cpp|34|undefined reference to Graph<int, Weight, Vertex>::Graph(bool)'|
/home/cx3/Pulpit/CB/Graph/main.cpp|41|undefined reference to Graph<int, Weight, Vertex>::AddVertex(Vertex<int>*)'| /home/cx3/Pulpit/CB/Graph/main.cpp|42|undefined reference to Graph<int, Weight, Vertex>::AddVertex(Vertex<int>*)'|
/home/cx3/Pulpit/CB/Graph/main.cpp|43|undefined reference to Graph<int, Weight, Vertex>::AddEdge(Vertex<int>*, Vertex<int>*, Weight*)'| /home/cx3/Pulpit/CB/Graph/main.cpp|50|undefined reference to Graph<int, Weight, Vertex>::GetMaxWeight()'|
||=== Build finished: 5 errors, 0 warnings ===|

Skoro dołączyłem plik nagłówkowy to z jakiej paki kompilator nie potrafi znaleźć publicznych metod klasy Graph? W pliku z kodem źródłowym klasa Graph ma określoną listę szablonów, w pliku nagłówkowym lista szablonów jest zgodna. Na czym polega mój błąd?

1

@cx3 na tym że nie wolno tak robić po prostu :) Jak masz klasę szablonową to implementacje metod muszą być w pliku nagłówkowym i tyle. Były kiedyś jakies pomysły żeby dało się to dzielić tak jak normalne klasy ale nic z tego nie wyszło.

1

Skoro dołączyłem plik nagłówkowy to z jakiej paki kompilator nie potrafi znaleźć publicznych metod klasy Graph?
Bo C++ jest zje... i nie widzi funkcji szablonowych jeśli są zdefiniowane w odrębnej jednostce kompilacji (czytaj: w innym pliku .cpp niż aktualnie kompilowany). Dlatego definicja (oprócz deklaracji) musi być w .h.

Prawdziwa przyczyna problemu jest trochę bardziej złożona.

Na czym polega mój błąd?
Nie twój, tylko twórców C++. Zrób tak jak ci spartanPAGE napisał: wszystko wrzuć do .h.

2
Azarien napisał(a):

Bo C++ jest zje... i nie widzi funkcji szablonowych jeśli są zdefiniowane w odrębnej jednostce kompilacji (czytaj: w innym pliku .cpp niż aktualnie kompilowany). Dlatego definicja (oprócz deklaracji) musi być w .h.

Prawdziwa przyczyna problemu jest trochę bardziej złożona.

Na czym polega mój błąd?
Nie twój, tylko twórców C++.

C++ (czytaj: Standard) mówi, że źródło szablonu nie jest wymagane - to co piszesz to po prostu nie jest prawda. To, czy źródło jest wymagane to kwestia implementacji.

C++11 2.2.8 napisał(a)

It is implementation-defined whether the source of the translation units containing these definitions is required to be available.

Kompilator może sobie to zrobić jak mu się żywnie podoba, ma działać. To, że większość kompilatorów po prostu wymaga dostępu do źródła to nie wina języka. Mylisz język (definicję, czyli standard) z implementacjami.

Azarien napisał(a):

Bo C++ jest zje...

Świetny argument.

1

Dziękuję wszystkim za porady. Po prostu klasy szablonowe wraz z ich ciałami w pliku .hpp i plik .cpp jest zbędny. Za żadne pieniądze sam bym na to nie wpadł. Jeszcze raz dziękówa :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1