【问题标题】:Why the compiler is generating errors which aren't errors at all为什么编译器会生成根本不是错误的错误
【发布时间】:2022-01-23 00:55:04
【问题描述】:

我试图从优秀的书籍Crafting Interpreters 中用 C++ 编写我自己的 VM 实现。

本书构建了一个基于堆栈的虚拟机,我正在编写一个C++版本

所以这是编译器对我大喊大叫的代码。

object.h

#pragma once

#include "common.h"
#include "value.h"
#include "chunk.h"

#define OBJ_TYPE(value)        (AS_OBJ(value)->type)

#define IS_CLOSURE(value)      isObjType(value, OBJ_CLOSURE)
#define IS_FUNCTION(value)     isObjType(value, OBJ_FUNCTION)
#define IS_NATIVE(value)       isObjType(value, OBJ_NATIVE)
#define IS_STRING(value)       isObjType(value, OBJ_STRING)

#define AS_CLOSURE(value)      ((ObjClosure*)AS_OBJ(value))
#define AS_FUNCTION(value)     ((ObjFunction*)AS_OBJ(value))
#define AS_NATIVE(value)       (((ObjNative*)AS_OBJ(value))->function)
#define AS_STRING(value)       ((ObjString*)AS_OBJ(value))
#define AS_CSTRING(value)      (((ObjString*)AS_OBJ(value))->chars)

typedef enum {
    OBJ_CLOSURE,
    OBJ_FUNCTION,
    OBJ_NATIVE,
    OBJ_STRING,
    OBJ_UPVALUE
} ObjType;

struct Obj {
    ObjType type;
    Obj* next;
};

struct ObjString :Obj {
    int length;
    char* chars;
    uint32_t hash;
};

struct ObjFunction :Obj {
    int arity;
    int upvalueCount;
    Chunk chunk;
    ObjString* name;
};

struct ObjUpvalue :Obj {
    Value* location;
};

struct ObjClosure :Obj {
    ObjFunction* function;
    ObjUpvalue** upvalues;
    int upvalueCount;
};

typedef Value(*NativeFn)(int, Value*);

struct ObjNative :Obj {
    NativeFn function;
};

ObjUpvalue* newUpvalue(Value* slot);
ObjClosure* newClosure(ObjFunction* function);
ObjFunction* newFunction();
ObjNative* newNative(NativeFn function);
ObjString* takeString(char* chars, int length);
ObjString* copyString(const char* chars, int length);
void printObject(Value value);

static inline bool isObjType(Value value, ObjType type) {
    return IS_OBJ(value) && AS_OBJ(value)->type == type;
}

common.h

#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define DEBUG_PRINT_CODE
#define DEBUG_TRACE_EXECUTION

#define UINT8_COUNT (UINT8_MAX + 1)

值.h

#pragma once

#include "common.h"
#include "object.h"

typedef enum {
    VAL_BOOL,
    VAL_NIL,
    VAL_NUMBER,
    VAL_OBJ
} ValueType;

#define IS_BOOL(value)    ((value).type == VAL_BOOL)
#define IS_NIL(value)     ((value).type == VAL_NIL)
#define IS_NUMBER(value)  ((value).type == VAL_NUMBER)
#define IS_OBJ(value)     ((value).type == VAL_OBJ)

#define AS_OBJ(value)     ((value).as.obj)
#define AS_BOOL(value)    ((value).as.boolean)
#define AS_NUMBER(value)  ((value).as.number)

#define BOOL_VAL(value)   (Value {.type = VAL_BOOL, .as = {.boolean = value}})
#define NIL_VAL           (Value {.type = VAL_NIL, .as = {.number = 0}})
#define NUMBER_VAL(value) (Value {.type = VAL_NUMBER, .as = {.number = value}})
#define OBJ_VAL(object)   (Value {.type = VAL_OBJ, .as = {.obj = (Obj*)object}})

struct Value {
    ValueType type;
    union {
        bool boolean;
        double number;
        Obj* obj;
    } as;
    bool operator==(Value b);
};

struct ValueArray {
    int count;
    int capacity;
    Value* values;

    ValueArray();
    ~ValueArray();
    void write(Value value);
};

void printValue(Value value);
void freeValueArray(ValueArray* array);

chunk.h

#pragma once

#include "common.h"
#include "value.h"

typedef enum {
    OP_CONSTANT,
    OP_NIL,
    OP_TRUE,
    OP_FALSE,
    OP_POP,
    OP_GET_LOCAL,
    OP_SET_LOCAL,
    OP_GET_GLOBAL,
    OP_DEFINE_GLOBAL,
    OP_SET_GLOBAL,
    OP_GET_UPVALUE,
    OP_SET_UPVALUE,
    OP_EQUAL,
    OP_GREATER,
    OP_LESS,
    OP_NEGATE,
    OP_ADD,
    OP_SUBTRACT,
    OP_MULTIPLY,
    OP_DIVIDE,
    OP_NOT,
    OP_PRINT,
    OP_JUMP,
    OP_JUMP_IF_FALSE,
    OP_LOOP,
    OP_CALL,
    OP_CLOSURE,
    OP_CLOSE_UPVALUE,
    OP_RETURN
} OpCode;

struct Chunk {
    int count;
    int capacity;
    uint8_t* code;
    int* lines;
    ValueArray constants;

    Chunk();
    ~Chunk();
    void write(uint8_t byte, int line);
    int addConstant(Value value);
};

在编译这些文件和其他一些文件时,我收到以下错误消息

Build started...
1>------ Build started: Project: Clox, Configuration: Debug x64 ------
1>chunk.cpp
1>D:\Ankit\Programming\C++\Clox\object.h(45,8): error C3646: 'chunk': unknown override specifier
1>D:\Ankit\Programming\C++\Clox\object.h(45,13): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>D:\Ankit\Programming\C++\Clox\object.h(51,7): error C2143: syntax error: missing ';' before '*'
1>D:\Ankit\Programming\C++\Clox\object.h(51,7): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>D:\Ankit\Programming\C++\Clox\object.h(51,17): error C2238: unexpected token(s) preceding ';'
1>D:\Ankit\Programming\C++\Clox\object.h(61,15): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>D:\Ankit\Programming\C++\Clox\object.h(61,16): error C2065: 'NativeFn': undeclared identifier
1>D:\Ankit\Programming\C++\Clox\object.h(61,24): error C2513: 'int': no variable declared before '='
1>D:\Ankit\Programming\C++\Clox\object.h(61,24): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>INTERNAL COMPILER ERROR in 'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\HostX64\x64\CL.exe'
1>    Please choose the Technical Support command on the Visual C++
1>    Help menu, or open the Technical Support help file for more information
1>Done building project "Clox.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

我不明白为什么会出现这些错误。

【问题讨论】:

  • 您不需要在 C++ 中对结构进行 typedef - 除了在 C 中 struct S {} 直接将 S 添加到周围的命名空间中。枚举也一样。使用这些 typedef 所实现的只是使代码更难阅读......除了你真的应该产生一个minimal reproducible example:逐步丢弃任何无助于重现错误的代码(这样做时你可能会发现自己已经犯错了......)。
  • 看起来你所有的 Xyz* newXyz(...) 函数实际上应该是构造函数。
  • 我会说 999/1000 次,你是错误的,而不是编译器。我昨天刚谈到这个。当然,这是关于标准库的。
  • 需要 ObjType 枚举作为 Obj 中的成员通常暗示 C++ 中存在设计缺陷。您不应该需要它,而是您宁愿提供虚函数作为通用接口——根据它们实际应该做的事情在继承类中被覆盖。然后从其他地方调用这些虚函数,而不知道对象的具体类型。
  • 头文件中的代码看起来像C代码。这段代码是否正在从 C 移植到 C++?

标签: c++ visual-studio compiler-errors crafting-interpreters


【解决方案1】:

您的包含文件中有一个循环。

object.h  => chunk.h => value.h => object.h

所以你得到的声明并不是所有其他类型都已定义(因为#pragma once 已经阻止了包含递归地发生)。

您需要通过在其中一个文件中使用前向声明并删除#include 来打破循环。


如果没有所有文件,就很难重现问题。但我认为这可以通过以下更改来解决(在 value.h 中)

1:删除此包括:

#pragma once

#include "common.h"
// -> Remove this line #include "object.h"

2:添加前向声明:

struct Obj;     // Forward declare the class Obj
struct Value {
    ValueType type;
    union {
        bool boolean;
        double number;
        Obj* obj;
    } as;
    bool operator==(Value b);
};

从头文件中包含的一般规则是:

  1. 谨慎行事,只包含您需要的内容。
  2. 如果您不需要完整的类型信息,请转发声明而不是包含。
    即,如果您只使用指针,则前向声明该类。

这可能意味着源文件将需要一个额外的包含行,但这没关系,因为您通常不包含源文件,您不会以循环结束。


旁注还有其他一些奇怪的事情你正在做。


typedef 放在所有结构的前面。

`typedef struct Value { /* STUFF */} Value;

这是 C 代码,在 C++ 中不需要。你可以这样做:

`struct Value { /* STUFF */};

并具有相同的效果。


当可以使用普通函数时不要使用宏。

// There is no type checking here.
// This is literally text replacement and can go wrong so easily.
#define IS_BOOL(value)    ((value).type == VAL_BOOL)

// This is type checked.
// Will more than likely be inclined by the compiler so is
// no more expensive.
inline bool isBool(Value const& value) {return value.type == VA_BOOL;}

当可以使用 const 表达式时不要使用宏。

#define UINT8_COUNT (UINT8_MAX + 1)

static constexpr std::uint8_t UINT8_COUNT = (UINT8_MAX + 1);

更多未正确类型检查的宏魔术:

#define BOOL_VAL(value)   (Value {.type = VAL_BOOL, .as = {.boolean = value}})

在这种情况下,一组适当的构造函数将解决这个问题。而且您不需要依赖于放置正确的宏,编译器会检查类型并分配使用正确的值。


不要创建自己的数组类型:

struct ValueArray {
    int count;
    int capacity;
    Value* values;

    ValueArray();
    ~ValueArray();
    void write(Value value);
};

该标准已经定义了一些很好的替代方案,并且工作效率很高(std::vector&lt;&gt;std::array&lt;&gt; 和其他一些)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-18
    相关资源
    最近更新 更多