Step 6: Creating the Generic Component Class

Read time: 13 minutes (3404 words)

With the Wire and Pin classes working, building a generic Component class will be pretty easy. The component is very much like the wire, only components need a number of Pins, and we need to define each pin as either an input, or an output. The Wire class only provided one “input” pin, but we may need more inputs to our components.

That simply means we need to use that Vector class on both inputs and outputs. We could set up a general pin attachment, but then we would need a way to identify if a pin is an input or an output. By separating them this way, we know which is which.

Note

In microcontrollers like the ATtiny85, some of those devices pins may be programmed to be inputs or outputs, depending on the needs of the code. The scheme we have here is fine for the internals of the processor, but we would need to rethink things if we handle those I/O pins on the chip. The pins we are working with now, end up inside the processor, and are not to be changes once they are set up!

Component Class Specification

Here is the needed header file for our new class:

include/Component.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Copyright 2018 Roie R. Black
#pragma once

#include <string>
#include <vector>

#include "Component.h"
#include "Pin.h"


class Component {
 public:
    // constructor
    explicit Component(std::string n);

    // mutators
    void add_in_pin(std::string name);
    void add_out_pin(std::string name);
    void tick(void);

    // accessors
    std::string get_name(void);
    Pin * get_in_pin(std::string n);
    Pin * get_out_pin(std::string n);

 private:
    std::string name;
    std::vector<Pin *> in_pins;
    std::vector<Pin *> out_pins;
};

Here, we have provided methods that can be used to add new input pinsa and output pins. We also provide a dummy tick routine.

We have one significant issue with this design. Each pin we connect to a component will have a name, but we will connect a pin to a component the same way we attached a wire to a pin, using pointers. Once we have attached all the pins, we need to come up with a way to find a specific pin when we want to do some internal processing. How are we going to do that?

The answer is simple, we need to search for the right pin using the pin’s name. That means we need methods 5o look up pin by name, one to search input pins only, and one to search output pins only. These routines return the address of the pin we are after.

Testing the Component Class

When we ponder testing this class, we quickly discover it will not be as easy as testing the wire. A Component has a bunch of pins connected to it. We do not attach wires directly to the component, we attach wires to the pins. Those pins are logically part of the component.

The components we will actually build read input signals sitting on the input pins, process those signals, then write output signal results to the output pins. All of this action happens when we call the tick method.

In our generic component class, there is nothing to do in the tick method, real actions will happen in derived classes. That means testing the tick method is this particular class is not necessary.

Testing Pin Attachments

We have defined two methods for dealing with pins: one that adds a new pin to the component, and one that fetches the address of a pin from the component using the name assigned. We can use these two methods to test attaching pins on both the input side and the output sides.

What we will do is create a pin, giving it a name. We will set up the add pin routines so they initialize the pin value to some random number (42 will do!). Then we will then ask the component to return the address of a named pin, and check the value stored on that pin to make sure it is 42!

Here is the new test code:

tests/test_component.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Copyright 2018 Roie R. Black
#include <catch.hpp>
#include "Component.h"
#include "Pin.h"

TEST_CASE( "test component constructor", "component" ){
    const std::string name = "basic";
    Component part(name);
    REQUIRE(part.get_name() == name);
}

TEST_CASE( "test component pin attachment", "component" ) {
    const std::string pname = "PIN1";
    const std::string cname = "comp1";
    Component part(cname);
    Pin pin(pname);
    part.add_in_pin(pname);
    REQUIRE( part.get_in_pin(pname)->get_val() == 42 );
    part.add_out_pin(pname);
    REQUIRE( part.get_out_pin(pname)->get_val() == 42 );
}

Component Class Implementation

Here is the implementation needed for this class:

lib/Component.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Copyright 2018 Roie R. Black
#include "Component.h"
#include "Pin.h"

// constructors
Component::Component(std::string n) {
    name = n;
}

// accessor methods

std::string Component::get_name(void) {
    return name;
}

Pin * Component::get_in_pin(std::string name) {
    for (int i = 0; i < in_pins.size(); i++) {
        if (in_pins[i]->get_name() == name) return in_pins[i];
    }
    return nullptr;
}

Pin * Component::get_out_pin(std::string name) {
    for (int i = 0; i < in_pins.size(); i++) {
        if (out_pins[i]->get_name() == name) return out_pins[i];
    }
    return nullptr;
}

// mutator methods
void Component::add_in_pin(std::string name){
    Pin * paddr = new Pin(name);
    paddr->set_val(42);
    in_pins.push_back(paddr);
}

void Component::add_out_pin(std::string name){
    Pin * paddr = new Pin(name);
    paddr->set_val(42);
    out_pins.push_back(paddr);
}


At this point, we have eight test cases written, and all tests are passing. We are making progress, even if our code does not do much yet.

Let’s move on and build a super-simple device that will do something useful. This will be easy! (Don’t worry, things will get a bit more interesting soon enough!)