I am looking for a way to get a specified templated struct from the custom Node classes I am building without having to static_cast or dynamic_cast when I get the pointers. What ways can I use to avoid that?
node.hpp
#define DATA_TEMPLATE template<typename T>
#ifndef FUNCTION_TEMPLATE
#define FUNCTION_TEMPLATE template<typename R, typename... T>
#define FUNCTION_TEMPLATE
Node(Node* prev = nullptr, Node* next = nullptr) : prev(prev), next(next) {}
virtual ~Node() = default;
virtual Node* getNext() const { return next; }
virtual void setNext(Node* next) { this->next = next; }
virtual Node* getPrev() const { return prev; }
virtual void setPrev(Node* prev) { this->prev = prev; }
virtual void print(std::ostream& os) const = 0;
struct DataNode : public Node {
DataNode(Node* prev, T dat, Node* next) : Node{ prev, next }, data(dat) {}
DataNode(Node* prev, T dat) : DataNode{ prev, dat, nullptr } {}
DataNode(T dat, Node* next) : DataNode{ nullptr, dat, next } {}
DataNode(T dat) : DataNode{ nullptr, dat, nullptr } {}
T getData() { return data; }
void setData(T newData) { data = newData; }
void print(std::ostream& os) const override { os << data; }
friend std::ostream& operator<<(std::ostream& os, const DataNode<T>& dn) { dn.print(os); return os; };
struct FunctionNode : public Node {
std::function<R(T...)> fn;
FunctionNode(Node* prev, std::function<R(T...)> func, Node* next) : Node{ prev, next }, fn(func) {}
FunctionNode(Node* prev, std::function<R(T...)> func) : FunctionNode{ prev, nullptr, func } {}
FunctionNode(std::function<R(T...)> func, Node* next) : FunctionNode{ nullptr, next, func } {}
FunctionNode(std::function<R(T...)> func) : FunctionNode{ nullptr, nullptr, func } {}
R operator()(T... args) { return fn(args...); }
<code>#pragma once
#ifndef NODE_H
#define NODE_H
// for ease of use
#ifndef DATA_TEMPLATE
#define DATA_TEMPLATE template<typename T>
#else
#define DATA_TEMPLATE
#endif
#ifndef FUNCTION_TEMPLATE
#define FUNCTION_TEMPLATE template<typename R, typename... T>
#else
#define FUNCTION_TEMPLATE
#endif
#include <iostream>
#include <string>
#include <functional>
#include <ostream>
#include <memory>
struct Node {
Node* prev;
Node* next;
Node(Node* prev = nullptr, Node* next = nullptr) : prev(prev), next(next) {}
virtual ~Node() = default;
virtual Node* getNext() const { return next; }
virtual void setNext(Node* next) { this->next = next; }
virtual Node* getPrev() const { return prev; }
virtual void setPrev(Node* prev) { this->prev = prev; }
virtual void print(std::ostream& os) const = 0;
};
DATA_TEMPLATE
struct DataNode : public Node {
T data;
DataNode(Node* prev, T dat, Node* next) : Node{ prev, next }, data(dat) {}
DataNode(Node* prev, T dat) : DataNode{ prev, dat, nullptr } {}
DataNode(T dat, Node* next) : DataNode{ nullptr, dat, next } {}
DataNode(T dat) : DataNode{ nullptr, dat, nullptr } {}
T getData() { return data; }
void setData(T newData) { data = newData; }
void print(std::ostream& os) const override { os << data; }
friend std::ostream& operator<<(std::ostream& os, const DataNode<T>& dn) { dn.print(os); return os; };
};
FUNCTION_TEMPLATE
struct FunctionNode : public Node {
std::function<R(T...)> fn;
FunctionNode(Node* prev, std::function<R(T...)> func, Node* next) : Node{ prev, next }, fn(func) {}
FunctionNode(Node* prev, std::function<R(T...)> func) : FunctionNode{ prev, nullptr, func } {}
FunctionNode(std::function<R(T...)> func, Node* next) : FunctionNode{ nullptr, next, func } {}
FunctionNode(std::function<R(T...)> func) : FunctionNode{ nullptr, nullptr, func } {}
R operator()(T... args) { return fn(args...); }
};
#endif
</code>
#pragma once
#ifndef NODE_H
#define NODE_H
// for ease of use
#ifndef DATA_TEMPLATE
#define DATA_TEMPLATE template<typename T>
#else
#define DATA_TEMPLATE
#endif
#ifndef FUNCTION_TEMPLATE
#define FUNCTION_TEMPLATE template<typename R, typename... T>
#else
#define FUNCTION_TEMPLATE
#endif
#include <iostream>
#include <string>
#include <functional>
#include <ostream>
#include <memory>
struct Node {
Node* prev;
Node* next;
Node(Node* prev = nullptr, Node* next = nullptr) : prev(prev), next(next) {}
virtual ~Node() = default;
virtual Node* getNext() const { return next; }
virtual void setNext(Node* next) { this->next = next; }
virtual Node* getPrev() const { return prev; }
virtual void setPrev(Node* prev) { this->prev = prev; }
virtual void print(std::ostream& os) const = 0;
};
DATA_TEMPLATE
struct DataNode : public Node {
T data;
DataNode(Node* prev, T dat, Node* next) : Node{ prev, next }, data(dat) {}
DataNode(Node* prev, T dat) : DataNode{ prev, dat, nullptr } {}
DataNode(T dat, Node* next) : DataNode{ nullptr, dat, next } {}
DataNode(T dat) : DataNode{ nullptr, dat, nullptr } {}
T getData() { return data; }
void setData(T newData) { data = newData; }
void print(std::ostream& os) const override { os << data; }
friend std::ostream& operator<<(std::ostream& os, const DataNode<T>& dn) { dn.print(os); return os; };
};
FUNCTION_TEMPLATE
struct FunctionNode : public Node {
std::function<R(T...)> fn;
FunctionNode(Node* prev, std::function<R(T...)> func, Node* next) : Node{ prev, next }, fn(func) {}
FunctionNode(Node* prev, std::function<R(T...)> func) : FunctionNode{ prev, nullptr, func } {}
FunctionNode(std::function<R(T...)> func, Node* next) : FunctionNode{ nullptr, next, func } {}
FunctionNode(std::function<R(T...)> func) : FunctionNode{ nullptr, nullptr, func } {}
R operator()(T... args) { return fn(args...); }
};
#endif
main.cpp
<code>#include <iostream>
// Some testing functions for the FunctionNode struct
double divideNums(double x, double y) {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
std::cout << node << std::endl;
Node* prevNode = node.getPrev();
Node* nextNode = node.getNext();
// Takes the previous and next nodes and checks if they can be dynamically
// cast to a DataNode pointer with the template argument "int".
// I want to avoid this if the user doesn't know what initial arguments there were
if (auto* pn = dynamic_cast<DataNode<int>*>(prevNode))
std::cout << *pn << std::endl;
std::cout << "Node is not a DataNode" << std::endl;
if (auto* nn = dynamic_cast<DataNode<int>*>(nextNode))
std::cout << *nn << std::endl;
std::cout << "Node is not a DataNode" << std::endl;
<code>#include <iostream>
#include <string>
#include <stdexcept>
#include "node.hpp"
// Some testing functions for the FunctionNode struct
int add(int x, int y) {
return x + y;
}
double divideNums(double x, double y) {
if (y > 0) {
return x / y;
} else {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
}
}
int main()
{
DataNode<int> node{
new DataNode(7),
3,
new DataNode{5}
};
std::cout << node << std::endl;
Node* prevNode = node.getPrev();
Node* nextNode = node.getNext();
// Takes the previous and next nodes and checks if they can be dynamically
// cast to a DataNode pointer with the template argument "int".
// I want to avoid this if the user doesn't know what initial arguments there were
// if possible
if (auto* pn = dynamic_cast<DataNode<int>*>(prevNode))
std::cout << *pn << std::endl;
else
std::cout << "Node is not a DataNode" << std::endl;
if (auto* nn = dynamic_cast<DataNode<int>*>(nextNode))
std::cout << *nn << std::endl;
else
std::cout << "Node is not a DataNode" << std::endl;
return 0;
}
</code>
#include <iostream>
#include <string>
#include <stdexcept>
#include "node.hpp"
// Some testing functions for the FunctionNode struct
int add(int x, int y) {
return x + y;
}
double divideNums(double x, double y) {
if (y > 0) {
return x / y;
} else {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
}
}
int main()
{
DataNode<int> node{
new DataNode(7),
3,
new DataNode{5}
};
std::cout << node << std::endl;
Node* prevNode = node.getPrev();
Node* nextNode = node.getNext();
// Takes the previous and next nodes and checks if they can be dynamically
// cast to a DataNode pointer with the template argument "int".
// I want to avoid this if the user doesn't know what initial arguments there were
// if possible
if (auto* pn = dynamic_cast<DataNode<int>*>(prevNode))
std::cout << *pn << std::endl;
else
std::cout << "Node is not a DataNode" << std::endl;
if (auto* nn = dynamic_cast<DataNode<int>*>(nextNode))
std::cout << *nn << std::endl;
else
std::cout << "Node is not a DataNode" << std::endl;
return 0;
}
I want to do what I am doing in main.cpp without having to use dynamic cast, such as
<code>#include <iostream>
// Some testing functions for the FunctionNode struct
double divideNums(double x, double y) {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
// A DataNode with a previous and next node, and a data value of 3
std::cout << node << std::endl; // prints "3" to the console
// Gets the previous and next nodes respectively with their given types
// and template arguments
auto* prevNode = node.getPrev();
auto* nextNode = node.getNext();
// So I can use the nodes as such
std::cout << prevNode << std::endl; // prints "7" to the console
std::cout << nextNode << std::endl; // prints "5" to the console
<code>#include <iostream>
#include <string>
#include <stdexcept>
#include "node.hpp"
// Some testing functions for the FunctionNode struct
int add(int x, int y) {
return x + y;
}
double divideNums(double x, double y) {
if (y > 0) {
return x / y;
} else {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
}
}
int main()
{
// A DataNode with a previous and next node, and a data value of 3
DataNode<int> node{
new DataNode(7),
3,
new DataNode{5}
};
std::cout << node << std::endl; // prints "3" to the console
// Gets the previous and next nodes respectively with their given types
// and template arguments
auto* prevNode = node.getPrev();
auto* nextNode = node.getNext();
// So I can use the nodes as such
std::cout << prevNode << std::endl; // prints "7" to the console
std::cout << nextNode << std::endl; // prints "5" to the console
return 0;
}
</code>
#include <iostream>
#include <string>
#include <stdexcept>
#include "node.hpp"
// Some testing functions for the FunctionNode struct
int add(int x, int y) {
return x + y;
}
double divideNums(double x, double y) {
if (y > 0) {
return x / y;
} else {
throw std::runtime_error("Zero Division Error: cannot divide by zero!");
}
}
int main()
{
// A DataNode with a previous and next node, and a data value of 3
DataNode<int> node{
new DataNode(7),
3,
new DataNode{5}
};
std::cout << node << std::endl; // prints "3" to the console
// Gets the previous and next nodes respectively with their given types
// and template arguments
auto* prevNode = node.getPrev();
auto* nextNode = node.getNext();
// So I can use the nodes as such
std::cout << prevNode << std::endl; // prints "7" to the console
std::cout << nextNode << std::endl; // prints "5" to the console
return 0;
}
I’ve tried using the “auto” keyword, though I know that isn’t a proper solution, maybe. I’ve tried a few things to avoid the extra lines of code to no avail.
I can get the Nodes, but outputting them just gives me this (Output from main.cpp)
3
00000232104D8850
00000232104D8130
Which is the value of the main node, but the previous and next nodes addresses only