我正在尝试用C ++实现自己的编程语言。
当我的语言出现错误时(例如,在词法分析期间),抛出一个异常,由main函数捕获,然后打印其消息。
异常可以是不同类型:SyntaxError
或FunctionError
等等,所有这些都基于Error
类,因此即使属于不同类型,它们也可以被捕获
无论如何,每个子类的错误都会产生不同类型的错误,每个错误都需要在抛出错误以构造错误消息时发生的事情有不同的信息。
为了解决这个问题,我考虑了这个解决方案:
//Error contains a public "message" attribute of type std::string, and a constructor to initialize that string
class SyntaxError: public Error {
SyntaxError(int code, void* infos[]) {
Error("SintaxError: ");
switch (code) {
//each sub error has its own code and based on that the function assumes the pointers to the objects needed to construct the error message are stored in the infos array
case 1: //Unfinished string
//it assumes the line at which the error is thrown is stored in the first position of the passed array
message += "Unfinished string (at line " + std::to_string(*((int*) infos[0])) + ").";
case 2: //Malformed number
...
... //other errors, each one assuming in the infos array are stored pointers to different types of objects
}
}
};
然而,当您想要抛出错误时,这需要一个很好的设置(因为您必须确定要抛出的错误类型,并基于此创建一个指向所需信息的指针数组)并且它也非常混乱构造函数本身:我甚至无法命名所需的对象,因为我无法在switch语句中声明它们,我应该在函数的开头声明所有这些对象。
所以我选择了另一个解决方案:用所需的对象重载构造函数,这样当我想抛出一个未完成的字符串错误时,我只需传递int
,该行,当我想抛出格式错误的号码错误时,我必须传递int
和一个string
(格式错误的号码),等等上。
关键是,会出现错误需要相同数量和类型的信息,例如无效的转义序列错误中使用的转义序列,例如,需要{{1 }}(行)和int
(使用的转义序列),就像格式错误的数字重载一样,我不知道如何区分它们。
那么,有没有办法告诉编译器哪个函数在具有相同参数列表的不同函数之间基于我传递给函数的整数来调用?
答案 0 :(得分:1)
简短回答
您正在寻找的技术名为标签调度。
答案很长
使用int
s(和std::integral_constant
来分派请求?)在某种程度上难以阅读,我宁愿定义一个枚举类并使用模板方法。
举个例子:
enum class Tag { FOO, BAR };
然后你主要有两个选择:
模板方法和完整专业化:
template<Tag>
void myMethod(int arg1, char arg2);
template<>
void myMethod<Tag::FOO>(int arg1, char arg2) {}
template<>
void myMethod<Tag::BAR>(int arg1, char arg2) {}
被调用为:
myMethod<Tag::FOO>(42, 'c');
标签调度:
void myMethod(tag<Tag::FOO>, int arg1, char arg2) {}
void myMethod(tag<Tag::BAR>, int arg1, char arg2) {}
被调用为:
myMethod(tag<FOO>{}, 42, 'c');
在这种情况下,您必须在某处定义tag
结构。举个例子:
template<Tag>
struct tag {};
注意:正如评论中所提到的,这两种解决方案都适用于普通的枚举。
答案 1 :(得分:0)
那么如何以不同的方式解决这个问题呢?
在解析/ lexing /代码生成等过程中,保留一个Context对象(例如可以封装行号,列号和文件名?)。抛出异常时,传递Context对象。
在堆栈的下方,使用嵌套异常捕获并重新抛出异常(再次从调用堆栈中该位置的上下文创建)。
然后,当您打印出错误消息时,请通过迭代嵌套异常来执行此操作。
示例:
import scala.reflect.runtime.universe
// I receive the suite and test names as input
val suite = "com.mycomp.StackSuite"
val test = "stackShouldPopValuesIinLastInFirstOutOrder"
val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
val module = runtimeMirror.staticModule(suite)
val obj = runtimeMirror.reflectModule(module)
val status = obj.asInstanceOf[TestSuite].execute(test)
预期产出:
#include <stdexcept>
#include <exception>
#include <iostream>
#include <string>
struct context
{
std::string file;
int line;
};
struct context_exception : std::runtime_error
{
context_exception(std::string message, const context& ctx)
: std::runtime_error(message + " at line " + std::to_string(ctx.line) + " in file " + ctx.file)
{}
};
struct syntax_error : context_exception
{
syntax_error(const context& ctx)
: context_exception("syntax error", ctx)
{}
};
struct foo_error : context_exception
{
foo_error(const context& ctx)
: context_exception("foo error", ctx)
{}
};
void parse_x(context my_context)
{
my_context.line = 250;
throw syntax_error(my_context);
}
void parse_y(context my_context)
{
my_context.line = 200;
try {
parse_x(my_context);
}
catch(const std::exception& e)
{
std::throw_with_nested(foo_error(my_context));
}
}
// trivial example of unwrapping an exception:
void print_exception(const std::exception& e, int level = 0)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
} catch(const std::exception& e) {
print_exception(e, level+1);
} catch(...) {}
}
int main()
{
context ctx { "foo.cpp", 200 };
try {
parse_y(ctx);
}
catch(const std::exception& e)
{
print_exception(e);
}
}