我目前正在嘗試用 C 實作一種編程語言。在決議階段之后,我有一個可以操作的抽象語法樹,其中包括型別檢查和位元組碼生成。之后,在這棵樹上運行不同的分析類,如 ASTPrinter 和前面提到的型別檢查器。
以前,訪問者類的visit()方法回傳 void,但我最近意識到有些訪問者可能需要回傳一些值。我嘗試使用模板,但我遇到了與靜態時間多型性和運行時多型性有關的問題,因為我從這里了解到:C 虛擬模板方法。
以下是與此相關的類(我知道它無法編譯,但它說明了我正在嘗試做的事情):
運算式.h
class Expression {
public:
template<typename R> R accept(ExprVisitor<R>& visitor);
};
ExprVisitor.h
template<typename R>
class ExprVisitor {
public:
virtual R visitAssignmentExpression(class Assignment* expression) = 0;
virtual R visitBinaryExpression(class Binary* expression) = 0;
// Rest of the visit methods...
};
示例運算式類 (Assignment.h)
class Assignment: public Expression {
public:
template<typename R> R accept(ExprVisitor<R>& visitor);
};
示例訪問者類 (ASTPrinter.h)
class ASTPrinter: public ExprVisitor<std::string> {
public:
std::string visitAssignmentExpression(Assignment* expression) override;
std::string visitBinaryExpression(Binary* expression) override;
// Rest of the visit methods...
};
如圖所示,ASTPrinter 需要回傳一個std::string. 我相信這個問題源于該accept()方法不是虛擬的Expression.h(因為我使用的是模板)。
這是我收到的確切錯誤訊息(針對每種 AST 節點型別重復):
undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > Expression::accept<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(ExprVisitor<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)'
我的問題是:有沒有另一種方法來實作同樣的目標?我已經堅持了很長時間,我很感激任何幫助。謝謝
最小可重現示例:
主檔案
#include "Literal.h"
#include "Binary.h"
#include "ASTPrinter.h"
int main(int argc, char** argv) {
Binary expr = Binary(Literal("10"), " ", Literal("10"));
ASTPrinter ast = ASTPrinter();
ast.constructTree(expr);
}
運算式.h
#ifndef CODEPULSAR_EXPRESSION_H
#define CODEPULSAR_EXPRESSION_H
#include "ExprVisitor.h"
class Expression {
public:
template<typename R> R accept(ExprVisitor<R>& visitor);
};
#endif
ExprVisitor.h
#ifndef CODEPULSAR_EXPRVISITOR_H
#define CODEPULSAR_EXPRVISITOR_H
template<typename R> class ExprVisitor {
public:
virtual R visitBinaryExpression(class Binary expression) = 0;
virtual R visitLiteralExpression(class Literal expression) = 0;
};
#endif
AST列印機。*
// ASTPrinter.h
#ifndef CODEPULSAR_ASTPRINTER_H
#define CODEPULSAR_ASTPRINTER_H
#include <string>
#include <iostream>
#include "ExprVisitor.h"
#include "Expression.h"
#include "Binary.h"
#include "Literal.h"
class ASTPrinter: public ExprVisitor<std::string> {
public:
void constructTree(Expression ast);
// Expression AST Visitors
std::string visitBinaryExpression(Binary expression) override;
std::string visitLiteralExpression(Literal expression) override;
};
#endif
// ASTPrinter.cpp
#include "ASTPrinter.h"
void ASTPrinter::constructTree(Expression ast) {
std::cout << ast.accept(*this) << std::endl;
}
std::string ASTPrinter::visitBinaryExpression(Binary expression) {
return "Binary(" expression.left.accept(*this) expression.operatorType expression.right.accept(*this);
}
std::string ASTPrinter::visitLiteralExpression(Literal expression) {
return "Literal(" expression.value ")";
}
二進制。*
// Binary.h
#ifndef CODEPULSAR_BINARY_H
#define CODEPULSAR_BINARY_H
#include <string>
#include "Expression.h"
class Binary: public Expression {
public:
Binary(Expression left, std::string operatorType, Expression right);
template<typename R> R accept(ExprVisitor<R>& visitor);
Expression left;
std::string operatorType;
Expression right;
};
#endif
// Binary.cpp
#include "Binary.h"
Binary::Binary(Expression left, std::string operatorType, Expression right) {
this->left = left;
this->operatorType = operatorType;
this->right = right;
}
template<typename R>
R Binary::accept(ExprVisitor<R>& visitor) {
visitor.visitBinaryExpression(this);
}
文字。*
// Literal.h
#ifndef CODEPULSAR_LITERAL_H
#define CODEPULSAR_LITERAL_H
#include <string>
#include "Expression.h"
class Literal: public Expression {
public:
Literal(std::string value);
template<typename R> R accept(ExprVisitor<R>& visitor);
std::string value;
};
#endif
// Literal.cpp
#include "Literal.h"
Literal::Literal(std::string value) {
this->value = value;
}
template<typename R>
R Literal::accept(ExprVisitor<R>& visitor) {
visitor.visitLiteralExpression(this);
}
uj5u.com熱心網友回復:
您將“out”引數添加到實際實作中Expression::accept并使用它來填充傳遞指向默認構造結果值的指標,并在模板函式中創建結果值:
class Assignment;
class Binary;
class BaseVisitor
{
public:
virtual void visitAssignmentExpression(Assignment& expression, void* context) = 0;
virtual void visitBinaryExpression(Binary& expression, void* context) = 0;
// Rest of the visit methods...
};
template<class T, class R>
class WrapperVisitor : public BaseVisitor
{
public:
WrapperVisitor(T& wrapped) noexcept
: m_wrapped(wrapped)
{
}
void visitAssignmentExpression(Assignment& expression, void* context) override
{
R* result = static_cast<R*>(context);
*result = m_wrapped.visitAssignmentExpression(expression);
}
void visitBinaryExpression(Binary& expression, void* context) override
{
R* result = static_cast<R*>(context);
*result = m_wrapped.visitBinaryExpression(expression);
}
// Rest of the visit methods...
private:
T& m_wrapped;
};
template<class T>
class WrapperVisitor<T, void> : public BaseVisitor
{
public:
WrapperVisitor(T& wrapped) noexcept
: m_wrapped(wrapped)
{
}
void visitAssignmentExpression(Assignment& expression, void*) override
{
m_wrapped.visitAssignmentExpression(expression);
}
void visitBinaryExpression(Binary& expression, void*) override
{
m_wrapped.visitBinaryExpression(expression);
}
// Rest of the visit methods...
private:
T& m_wrapped;
};
template<class T>
T& RefVal()
{
static_assert(sizeof(T) != sizeof(T), "for use in unevaluated context only");
}
template<class T>
concept Visitor = requires(T visitor, Assignment & a, Binary & b)
{
std::same_as<decltype(visitor.visitAssignmentExpression(a)), decltype(visitor.visitBinaryExpression(b))>;
};
template<class T>
concept VoidVisitor = requires(T visitor, Assignment& a, Binary& b)
{
requires Visitor<T>;
{visitor.visitAssignmentExpression(a) } -> std::same_as<void>;
};
class Expression {
public:
template<Visitor Visitor> requires (!VoidVisitor<Visitor>)
auto accept(Visitor& visitor)
{
using ResultType = decltype(std::declval<Visitor>().visitAssignmentExpression(RefVal<Assignment>()));
ResultType result;
WrapperVisitor<Visitor, ResultType> wrapperVisitor(visitor);
acceptImpl(wrapperVisitor, &result);
return result;
}
template<VoidVisitor Visitor>
void accept(Visitor& visitor)
{
// check the return types are the same
static_assert(std::is_void_v<decltype(std::declval<Visitor>().visitBinaryExpression(RefVal<Binary>()))>);
WrapperVisitor<Visitor, void> wrapperVisitor(visitor);
acceptImpl(wrapperVisitor, nullptr);
}
protected:
virtual void acceptImpl(BaseVisitor& visitor, void* context) = 0;
};
class Assignment : public Expression {
protected:
virtual void acceptImpl(BaseVisitor& visitor, void* context) override
{
visitor.visitAssignmentExpression(*this, context);
}
};
class Binary : public Expression {
protected:
virtual void acceptImpl(BaseVisitor& visitor, void* context) override
{
visitor.visitBinaryExpression(*this, context);
}
};
class ASTPrinter {
public:
std::string visitAssignmentExpression(Assignment& expression)
{
return "Assignment";
}
std::string visitBinaryExpression(Binary& expression)
{
return "Binary";
}
// Rest of the visit methods...
};
int main(void) {
ASTPrinter printer;
Expression&& b = Binary();
Expression&& a = Assignment();
std::cout << b.accept(printer) << '\n'
<< a.accept(printer) << '\n';
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/496359.html
上一篇:c 入門加部分專業化問題
