Before this project, my only experience with C++ was with some basic arduino programming. I decided to suck it up and start working with C++ for my projects rather than trying to make python work for things it shouldn't, so I'm trying to figure out a lot of best practice things and simple issues.
I have written a program that has many classes inside of it. The classes have functions which do many things, but it may be important to note that some functions start threads or are continually looping inside a thread. I often need to call functions from another class from a different class, but that seems to be causing me issues. What I've been doing now is creating an object for that class I need to reference inside of the class I'm currently in, but that is causing issues with the order I put my classes in my code, for example:
(347,13): error C2079: 'anim' uses undefined class 'animation'
This error occurs in this section of the code:
class metronome {
private:
changeTempo tempo;
animation anim;
int lastManualTap = 0;
void beat() {
if (started) {
std::cout << "-------- " << j << "\n";
j++;
if (j == (globalBeatsPerMeasure + 1)) { j = 1; }
}
else {
std::cout << "--------\n";
}
}
//runs the click at a set tempo without user input
void autoMetro(float bpm) {
//global variable to stop the auto metro loop
while (autoActive) {
int bpmdelay = (60.0 / bpm) * 1000;
if ((clock() - lastBeatTime) >= bpmdelay) {
subdivideActive = false;
isFirst = false;
beat(); //console output
startSub(bpmdelay); //start subdivion
tempo.setBpm(bpm); //sets auto tempo to manual tempo
lastBeatTime = clock();
lastAutoBeatTime = clock();
}
}
return;
}
// outputs the subdivision at a set number of subdivisions per beat
void subdivide(int timeBetweenBeats) {
int timeBetweenSubdivision = timeBetweenBeats / globalSubdivision;
int b = 2;
int lastSubdivisionTime = clock();
subdivideActive = true;
while (subdivideActive == true) { // makes sure the subdivison is supposed to be on in case there is a manual tap before end of subdivision
//check if the amount of time between subdivisions has passed, print subdivision
if ((clock() - lastSubdivisionTime) >= timeBetweenSubdivision) {
lastSubdivisionTime = clock();
std::cout << b << "\n";
b++;
}
//when completed with set amount of subdivisions per beat, break
if (b == (globalSubdivision + 1)) {
b = 2;
subdivideActive = false;
}
}
}
//start subdivision
void startSub(int delay) {
std::thread subthread(&metronome::subdivide, this, delay);
subthread.detach();
}
//sdfusdhfus this is all trash anyways
void manualMetro() {
char c;
while (1) {
if (fileLoaded) {
c = _getch();
if (c) {
if (c == 'b') {
if ((clock() - lastAutoBeatTime) >= 300) {
subdivideActive = false;
beat();
this->startSub(clock() - lastBeatTime);
if (!isFirst) {
globalBPM = tempo.averageBpm((clock() - lastManualTap));
}
isFirst = false;
}
lastManualTap = clock();
lastBeatTime = clock();
autoActive = false;
}
else if (c == ' ' && autoActive == false) {
started = true;
subdivideActive = false;
this->startAutoMetroThread(globalBPM);
}
else if (c == 'r' && started == true) {
anim.initializeAnimationFile("animation.txt");
}
}
}
}
}
// start the auto metro thread
void startAutoMetroThread(float bpm) {
autoActive = true;
std::thread autometrothread(&metronome::autoMetro, this, bpm);
autometrothread.detach();
}
public:
// start thread to control the metronome
void startManualMetrothread() {
std::thread keyboard(&metronome::manualMetro, this);
keyboard.detach();
}
};
//class that reads from the animation file
class animation {
private:
metronome metro;
std::string line;
public:
void initializeAnimationFile(std::string animationFileName) {
std::ifstream animationFile(animationFileName);
autoActive = false;
started = false;
subdivideActive = false;
if (animationFile.is_open())
{
while (std::getline(animationFile, line))
{
if (line.rfind("#", 0) == 0) {
if (line.rfind("#=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set bpm
else if (line.rfind("#bpm=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set beats per measure
else if (line.rfind("#beatsPerMeasure=", 0) == 0) {
line.erase(0, 17);
globalBeatsPerMeasure = std::stoi(line);
std::cout << globalBeatsPerMeasure << " beats per measure\n";
}
//set subdivision
else if (line.rfind("#subdivision=", 0) == 0) {
line.erase(0, 13);
globalSubdivision = std::stoi(line);
std::cout << globalSubdivision << " subdivisions\n";
}
//signal that the settings have been located
else if (line.rfind("#start", 0) == 0) {
fileLoaded = true;
std::cout << "Ready!\n";
}
else {
std::cout << line << " command not recognized!\n";
}
}
else if (line.rfind("&", 0) == 0) {
}
}
animationFile.close();
}
}
};
This is my full code:
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <time.h>
#include <stdio.h>
#include <iostream>
#include <thread>
#include <conio.h>
#include <atomic>
#include <fstream>
#include <string>
#include <sstream>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
//intialize global variables between threads
std::atomic<float> globalBPM(0);
std::atomic<int> globalSubdivision(0);
std::atomic<int> globalBeatsPerMeasure(0);
std::atomic<bool> isFirst(true);
std::atomic<bool> started(false);
std::atomic<bool> fileLoaded(false);
std::atomic<bool> autoActive(false);
std::atomic<int> lastAutoBeatTime(0);
std::atomic<int> lastBeatTime(0);
std::atomic<int> j(1);
std::atomic<bool> subdivideActive(false);
#define TCPListenerPort "17"
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
int iResult;
//array to hold ip addresses of each node at their id value
std::string nodeIP[100] = { "0" };
//array to hold battery voltage of each node at their id value
std::string nodeBatt[100] = { "0" };
class animation;
//class that handles all network transfers
class network {
private:
char recvbuf[DEFAULT_BUFLEN] = { 0 };
int recvbuflen = DEFAULT_BUFLEN;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
SOCKET Listen;
//thread that handles recieved connections from nodes
void TCPRequestRecvThread(SOCKET sock) {
do {
//get data from node, save it to string, output it
iResult = recv(sock, recvbuf, recvbuflen, 0);
if (iResult > 0) {
std::string str = recvbuf;
//printf("Bytes received: %d\n", iResult);
std::cout << str << "\n";
}
else if (iResult == 0) {
// printf("Connection closing :(((\n");
}
else {
printf("recv failed with error: %d D:\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
}
} while (iResult > 0);
//exit thread
return;
}
//start a thread to handle the node connection
void startTCPRequestRecvThread(SOCKET sk) {
std::thread t(&network::TCPRequestRecvThread, this, sk);
t.detach();
}
//loop thread that blocks until client connects and passes socket to recieve thread
void TCPListener(SOCKET Listen) {
while (true) {
// block and accept a client socket
ClientSocket = accept(Listen, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
WSACleanup();
}
//start communication thread
this->startTCPRequestRecvThread(ClientSocket);
}
}
public:
//start the TCP listener loop thread
void startTCPListenerThread(SOCKET sk) {
std::thread t(&network::TCPListener, this, sk);
t.detach();
}
SOCKET intializeTCPSocket() {
std::cout << "initil start\n";
struct addrinfo* result = NULL, * ptr = NULL, hints;
WSADATA wsaData;
//network tcp;
int iResult;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return INVALID_SOCKET;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, TCPListenerPort, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return INVALID_SOCKET;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return INVALID_SOCKET;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return INVALID_SOCKET;
}
freeaddrinfo(result);
//start listening on that socket
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return INVALID_SOCKET;
}
//return the setup listening socket
return ListenSocket;
}
};
//class that controls tempo changes
class changeTempo {
private:
int tapTimes[5] = { 0,0,0,0,0 };
int shiftArray[4];
int x = 0;
int add = 0;
public:
//change the average bpm when there is a manual user input
float averageBpm(int differance) {
//moves all the data down one position in the array so only 4 taps are averaged
if (x == 5) {
shift();
x = 4;
}
//store time between taps
tapTimes[x] = differance;
x++;
//add all the times together
add = 0;
for (int i = 0; i < 5; i++) {
add = add + tapTimes[i];
}
//find the number of user inputted taps in the array (so the bpm average is correct even if the user hasn't put in 4 beats before turning it to auto mode)
int num = 0;
for (int i = 0; i < 5; i++) {
if (tapTimes[i] > 0) { num++; }
}
//calculate and return bpm
return ((float)60 / (add / num))*1000;
}
//shift all the time between beats one position down (i know i'm lazy)
void shift() {
shiftArray[0] = tapTimes[1];
shiftArray[1] = tapTimes[2];
shiftArray[2] = tapTimes[3];
shiftArray[3] = tapTimes[4];
tapTimes[0] = shiftArray[0];
tapTimes[1] = shiftArray[1];
tapTimes[2] = shiftArray[2];
tapTimes[3] = shiftArray[3];
}
//set all of the times in the array to the auto time once auto is started
void setBpm(float bpm) {
for (int i = 0; i < 4; i++) {
tapTimes[i] = (60.0 / bpm) * 1000;
}
}
};
class metronome {
private:
changeTempo tempo;
animation anim;
int lastManualTap = 0;
void beat() {
if (started) {
std::cout << "-------- " << j << "\n";
j++;
if (j == (globalBeatsPerMeasure + 1)) { j = 1; }
}
else {
std::cout << "--------\n";
}
}
//runs the click at a set tempo without user input
void autoMetro(float bpm) {
//global variable to stop the auto metro loop
while (autoActive) {
int bpmdelay = (60.0 / bpm) * 1000;
if ((clock() - lastBeatTime) >= bpmdelay) {
subdivideActive = false;
isFirst = false;
beat(); //console output
startSub(bpmdelay); //start subdivion
tempo.setBpm(bpm); //sets auto tempo to manual tempo
lastBeatTime = clock();
lastAutoBeatTime = clock();
}
}
return;
}
// outputs the subdivision at a set number of subdivisions per beat
void subdivide(int timeBetweenBeats) {
int timeBetweenSubdivision = timeBetweenBeats / globalSubdivision;
int b = 2;
int lastSubdivisionTime = clock();
subdivideActive = true;
while (subdivideActive == true) { // makes sure the subdivison is supposed to be on in case there is a manual tap before end of subdivision
//check if the amount of time between subdivisions has passed, print subdivision
if ((clock() - lastSubdivisionTime) >= timeBetweenSubdivision) {
lastSubdivisionTime = clock();
std::cout << b << "\n";
b++;
}
//when completed with set amount of subdivisions per beat, break
if (b == (globalSubdivision + 1)) {
b = 2;
subdivideActive = false;
}
}
}
//start subdivision
void startSub(int delay) {
std::thread subthread(&metronome::subdivide, this, delay);
subthread.detach();
}
//sdfusdhfus this is all trash anyways
void manualMetro() {
char c;
while (1) {
if (fileLoaded) {
c = _getch();
if (c) {
if (c == 'b') {
if ((clock() - lastAutoBeatTime) >= 300) {
subdivideActive = false;
beat();
this->startSub(clock() - lastBeatTime);
if (!isFirst) {
globalBPM = tempo.averageBpm((clock() - lastManualTap));
}
isFirst = false;
}
lastManualTap = clock();
lastBeatTime = clock();
autoActive = false;
}
else if (c == ' ' && autoActive == false) {
started = true;
subdivideActive = false;
this->startAutoMetroThread(globalBPM);
}
else if (c == 'r' && started == true) {
anim.initializeAnimationFile("animation.txt");
}
}
}
}
}
// start the auto metro thread
void startAutoMetroThread(float bpm) {
autoActive = true;
std::thread autometrothread(&metronome::autoMetro, this, bpm);
autometrothread.detach();
}
public:
// start thread to control the metronome
void startManualMetrothread() {
std::thread keyboard(&metronome::manualMetro, this);
keyboard.detach();
}
};
//class that reads from the animation file
class animation {
private:
metronome metro;
std::string line;
public:
void initializeAnimationFile(std::string animationFileName) {
std::ifstream animationFile(animationFileName);
autoActive = false;
started = false;
subdivideActive = false;
if (animationFile.is_open())
{
while (std::getline(animationFile, line))
{
if (line.rfind("#", 0) == 0) {
if (line.rfind("#=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set bpm
else if (line.rfind("#bpm=", 0) == 0) {
line.erase(0, 5);
globalBPM = std::stof(line);
std::cout << globalBPM << " BPM\n";
}
//set beats per measure
else if (line.rfind("#beatsPerMeasure=", 0) == 0) {
line.erase(0, 17);
globalBeatsPerMeasure = std::stoi(line);
std::cout << globalBeatsPerMeasure << " beats per measure\n";
}
//set subdivision
else if (line.rfind("#subdivision=", 0) == 0) {
line.erase(0, 13);
globalSubdivision = std::stoi(line);
std::cout << globalSubdivision << " subdivisions\n";
}
//signal that the settings have been located
else if (line.rfind("#start", 0) == 0) {
fileLoaded = true;
std::cout << "Ready!\n";
}
else {
std::cout << line << " command not recognized!\n";
}
}
else if (line.rfind("&", 0) == 0) {
}
}
animationFile.close();
}
}
};
int main()
{
metronome metro;
animation ani;
network net;
SOCKET sk;
std::cout << "meow\n";
//initialize the TCP socket and check to see if it was initialized correctly
sk = net.intializeTCPSocket();
if (sk == INVALID_SOCKET) {
std::cout << "Error intializing socket!!\n";
}
//start the TCP listener thread
else {
net.startTCPListenerThread(sk);
}
metro.startManualMetrothread();
ani.initializeAnimationFile("animation.txt");
while (true) {}
}
I am not sure what is the correct fix for this issue