Reputation: 7895
This is a continuation of this previous question removing code duplication and user 1201programalarm suggested to use CRTP
to resolve my issue. I'm trying to take their advice and when I try to apply it to my Bus
class Visual Studio is generating compiler errors...
When I try to do this:
template<size_t BusSize>
class Bus : public Component, ComponentID<Bus> {
I get this error:
>------ Build started: Project: Simulator, Configuration: Debug x64 ------
1>main.cpp
1>c:\***\bus.h(6): error C3203: 'Bus': unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type
1>c:\***\bus.h(49): note: see reference to class template instantiation 'Bus<BusSize>' being compiled
1>Done building project "Simulator.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
And when I try to change it to this:
template<size_t BusSize>
class Bus : public Component, ComponentID<Bus<BusSize>> {
It then gives me these errors:
>------ Build started: Project: Simulator, Configuration: Debug x64 ------
1>main.cpp
1>c:\***\bus.h(11): error C2512: 'ComponentID<Bus<4>>': no appropriate default constructor available
1>c:\***\bus.h(6): note: see declaration of 'ComponentID<Bus<4>>'
1>c:\***\bus.h(11): note: while compiling class template member function 'Bus<4>::Bus(const std::string &)'
1>c:\***\main.cpp(10): note: see reference to function template instantiation 'Bus<4>::Bus(const std::string &)' being compiled
1>c:\***\main.cpp(10): note: see reference to class template instantiation 'Bus<4>' being compiled
1>c:\***\bus.h(11): error C2614: 'Bus<4>': illegal member initialization: 'ComponentID' is not a base or member
1>Done building project "Simulator.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I'm not sure what else to try to resolve these compiler errors...
Here is my source code...
main.cpp
#include <exception>
#include "Bus.h"
int main() {
try {
Wire w1, w2, w3, w4;
std::cout << w1.id() << " " << w2.id() << " " << w3.id() << " "<< w4.id() << "\n\n";
Bus<4> b1;
b1.connect(&w1, 0);
b1.connect(&w2, 1);
b1.connect(&w3, 2);
b1.connect(&w4, 3);
b1.connect(&w1, 1); // Valid Connection: same wire to multiple bus wires
b1.connect(&w1, 0); // Invalid Connection: trying to connect a same wire to the same bus wire multiple times...
std::cout << "\n";
Bus<4> b2;
w1.connect(&w2);
w2.connect(&w3);
w3.connect(&w4);
w4.connect(&w1);
std::cout << "\n";
b2.connect(&b1.wire(0), 0);
b2.connect(&b1.wire(1), 1);
b2.connect(&b1.wire(2), 2);
b2.connect(&b1.wire(3), 3);
std::cout << "\nThese are my connected components:\n";
for( size_t i = 0; i < b2.size(); i++ ) {
for (auto& component : b2.myConnections(i)) {
std::cout << "\t" << component->id() << " has ";
auto connections = component->myConnections();
for (auto& c : connections) {
std::cout << c->id() << " ";
}
std::cout << '\n';
}
std::cout << '\n';
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Component.h
#pragma once
#include <assert.h>
#include <memory>
#include <array>
#include <list>
#include <iomanip>
#include <iostream>
#include <string>
#include <sstream>
// I added this class template to handle the `ID's` as suggested to remove the code duplication...
template<class T>
class ComponentID {
public:
explicit ComponentID(std::string& id) {
static int i = 0;
std::stringstream strValue;
strValue << std::setw(3) << std::setfill('0') << std::to_string(i++);
id.append("_" + strValue.str());
}
};
class Component {
protected:
std::string id_ = "";
std::list<std::shared_ptr<Component>> components_;
explicit Component(const std::string& id) : id_{ id } {}
public:
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!\n";
return;
}
}
components_.push_back( std::make_shared<Component>( *other ) );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "\n";
}
virtual std::list<std::shared_ptr<Component>> myConnections() { return components_; }
virtual void propagate() {};
};
Wire.h
#pragma once
#include "Component.h"
// I added ComponentID as an inherited base,
// I removed the `updateID()` function from this class,
// and I removed the call to it from within the constructor
class Wire : public Component, ComponentID<Wire> {
private:
public:
explicit Wire(const std::string& name = "Wire") : Component(name), ComponentID(id_) {
//updateId();
};
virtual ~Wire() {}
virtual void propagate() override {
return;
}
};
Now, this is where I'm starting to have some issues...
Bus.h
#pragma once
#include "Wire.h"
// I'm trying to do the same as I did in my Wire class through inheritance...
// I also removed the updateID() function and the call to it with the constructor
// However, this is generating a compiler error.
template<size_t BusSize>
class Bus : public Component, ComponentID<Bus> {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), ComponentID(id_) {
createBus();
}
virtual ~Bus() {}
virtual void propagate() override {
return;
}
void connect(Component* component, size_t index) {
assert(index < BusSize);
assert(component != nullptr);
bus_interface_[index]->connect(component);
std::cout << "\t from " << component->id() << " to " << id_ << "\n";
}
Wire wire(size_t index) {
assert(index < BusSize);
return *bus_interface_[index].get();
}
std::array<std::shared_ptr<Wire>, BusSize> bus() { return bus_interface_; }
std::list<std::shared_ptr<Component>> myConnections(size_t index) {
assert(index < BusSize);
return bus_interface_[index]->myConnections();
}
size_t size() const { return BusSize; }
private:
void createBus() {
size_t i = 0;
for (auto& w : bus_interface_) {
w = std::shared_ptr<Wire>(new Wire);
}
}
};
Upvotes: 0
Views: 133
Reputation: 7895
I figured out a solution to my problem... I'm still puzzled as to why Visual Studio was giving me the compiler errors...
After looking at the code and seeing the pattern of the CRTP in use and without modifying it... I found an approach that works, and it works for me...
In my Bus
class, it itself is templated but not with a typename
type
... and this got me to thinking and knowing that the current CRTP
worked fine for the Wire
class that was non templated... I elected to do the following...
I created another abstract base class... that is a middle layer between Component
and Bus<size_t>
... And it is this non templated abstract class that inherits from both Component
and ComponentID
. Then I just have Bus<size_t>
inherit from BusAbstract
.
The rest of my code is still the same except for some minor changes within their constructors for auto naming - id generation... Here is the result of my Bus
class.
Bus.h
#pragma once
#include "Wire.h"
class BusAbstract : public Component, ComponentID<BusAbstract> {
protected:
explicit BusAbstract(const std::string& name = "Bus") : Component(name), ComponentID(id_) {}
};
template<size_t BusSize>
class Bus : public BusAbstract {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = ("Bus<" + std::to_string(BusSize) + ">") ) : BusAbstract(name) {
createBus();
}
virtual ~Bus() {}
virtual void propagate() override {
return;
}
void connect(Component* component, size_t index) {
assert(index < BusSize);
assert(component != nullptr);
bus_interface_[index]->connect(component);
std::cout << "\t from " << component->id() << " to " << id_ << "\n";
}
Wire wire(size_t index) {
assert(index < BusSize);
return *bus_interface_[index].get();
}
std::array<std::shared_ptr<Wire>, BusSize> bus() { return bus_interface_; }
virtual std::list<std::shared_ptr<Component>> myConnections(size_t index) {
assert(index < BusSize);
return bus_interface_[index]->myConnections();
}
size_t size() const { return BusSize; }
private:
void createBus() {
size_t i = 0;
for (auto& w : bus_interface_) {
w = std::shared_ptr<Wire>(new Wire(this->id() + "_Wire"));
}
}
};
And now I'm getting the desired behavior that I'm looking for without the "code-duplication" from the original question, and I'm able to do so with the use of this CRTP design.
Upvotes: 0
Reputation: 36379
Your second attempt at setting the base class is correct, you just need to add a call to the base class constructor as it doesn't have a default constructor:
template<size_t BusSize>
class Bus : public Component , ComponentID<Bus<BusSize>> {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), ComponentID<Bus<BusSize>>(id_) {
createBus();
}
Often a type alias is used to make this a bit simpler:
template<size_t BusSize>
class Bus : public Component , ComponentID<Bus<BusSize>> {
private:
using BusComponentID = ComponentID<Bus<BusSize>>;
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), BusComponentID(id_) {
createBus();
}
Upvotes: 1