I was trying to write my own VM implementation in C++ from the excellent book Crafting Interpreters.
The book builds a stack based virtual machine, of which I am writing a C++ version
So here is the code where the compiler is yelling at me.
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)
value.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);
};
When compiling these files along with some other files, I got the following error message
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 ==========
I can't understand why these errors are coming out.
You have a cycle in your include files.
object.h => chunk.h => value.h => object.h
So you are getting to a declaration were not all the other types have defined (because #pragma once have prevented the includes happening recursively).
You need to break the cycle by using forward declarations in one of these files and removing a #include.
It's hard to reproduce the problem without all the files. But I think this can be solved with the following change (in value.h)
1: Remove this include:
#pragma once
#include "common.h"
// -> Remove this line #include "object.h"
2: Add a forward declaration:
struct Obj; // Forward declare the class Obj
struct Value {
ValueType type;
union {
bool boolean;
double number;
Obj* obj;
} as;
bool operator==(Value b);
};
The general rule of including from a header file are:
Be judicious, only include what you need.
If you don't need the full type information, forward declare rather than include.
i.e. If you only use a pointer then forward declare the class.
This will probably mean that the source file will need an extra include line but that's OK as you normally don't include source file you don't end up with cycles.
Side-Note there are a couple of other odd things you are doing.
Putting typedef in-front of all your structures.
`typedef struct Value { /* STUFF */} Value;
This is C code and not needed in C++. You can simply do:
`struct Value { /* STUFF */};
and have the same effect.
Don't use macros when normal function's can be used.
// 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;}
Don't use macros when const expression can be used.
#define UINT8_COUNT (UINT8_MAX + 1)
static constexpr std::uint8_t UINT8_COUNT = (UINT8_MAX + 1);
More macro magic that is not correctly type checked:
#define BOOL_VAL(value) (Value {.type = VAL_BOOL, .as = {.boolean = value}})
In this case a proper set of constructors will solve this problem. And you don't need to rely on putting the correct macro in place the compiler will check the types and assign use the correct value.
Don't create your own array types:
struct ValueArray {
int count;
int capacity;
Value* values;
ValueArray();
~ValueArray();
void write(Value value);
};
The standard has some good alternatives already defined and that work very efficiently (std::vector<> or std::array<> and a few others).
Below you will find my code. First I present the header file then the corresponding cpp file. The error I receive is on the line of the constructor and states "expected an identifier" I have searched everywhere to find this error and I am stuck. It is also worth noting that I am not yet comfortable with classes in c++.
The header file:
#include <Eigen/Core>
#ifndef RIDGE_GD
#define RIDGE_GD
class RIDGE_GD
{
public:
RIDGE_GD(double lambda, double eta, double max_iter);
void fit(Eigen::MatrixXd X, Eigen::MatrixXd y);
double error();
Eigen::MatrixXd cost_grad();
void gd_step();
Eigen::MatrixXd X;
Eigen::MatrixXd y;
Eigen::MatrixXd w;
private:
double m_lambda;
double m_eta;
double m_max_iter;
};
#endif
The corresponding cpp file:
#include "RIDGE_GD.h"
#include <Eigen/Core>
RIDGE_GD::RIDGE_GD(double lambda, double eta, double max_iter)
{
m_lambda = lambda;
m_eta = eta;
m_max_iter = max_iter;
}
Eigen::MatrixXd RIDGE_GD::cost_grad(RIDGE_GD.X, RIDGE_GD.y, RIDGE_GD.w, RIDGE_GD.lambda) {};
void RIDGE_GD::gd_step()
{
}
void RIDGE_GD::fit(Eigen::MatrixXd X, Eigen::MatrixXd y)
{}
double RIDGE_GD::error()
{}
Your include guard uses the same identifier as your class name. So all instances of RIDGE_GD are replaced by nothing. The compiler sees your class as
class {
public:
(double lambda, double eta, double max_iter);
// ...
};
which is why you get the error.
Use a different identifier for your include guard. (Or change the name of your class.)
The header guard is wrong, it changes the class name to nothing:
#ifndef RIDGE_GD
#define RIDGE_GD
^^ This line changes `class RIDGE_GD` to `class `, as RIDGE_GD is defined to be empty
Instead use a proper header guard:
#ifndef RIDGE_GD_HEADER_INCLUDED
#define RIDGE_GD_HEADER_INCLUDED
...
#endif
Let say I've decleared this within MyTools.h
#ifndef _MYTOOLS_
#define _MYTOOLS_
typedef struct {
// const
double LN20;
double LN40;
// methods
double NoteToFrequency(int noteNumber);
} Tool;
extern const Tool tool;
#endif // !_MYTOOLS_
For every compilation unit, there is only a global/const/unique instance of Tool. Exactly what I want.
But now: how can I define it? In the .h i've only declared it. How can I define it in .cpp? Tried somethings like:
tool.LN20 = 1.34;
But of course it doesn't works. And the method's definition?
extern doesn't define any variable it just declares it. What you wan't to achieve can be done as below:
The link Global const object shared between compilation units explains how to do it with extern const
t.h file
#ifndef _MYTOOLS_
#define _MYTOOLS_
struct Tool {
// const
double LN20;
double LN40;
double NoteToFrequency(int noteNumber);
} ;
extern const Tool tool ;
#endif // !_MYTOOLS_
t1.cpp
#include "t.h"
#include <stdio.h>
void use_tool()
{
printf("%f\n",tool.LN20);
printf("%f\n",tool.LN40);
return;
}
t2.cpp
#include "t.h"
#include <stdio.h>
const Tool tool = {.LN20 = 20.0, .LN40 = 30.2};
double Tool::NoteToFrequency(int noteNumber)
{
return 12.0;
}
void use1_tool()
{
printf("%f\n",tool.LN20);
printf("%f\n",tool.LN40);
return;
}
int main()
{
void use_tool();
use_tool();
use1_tool();
return 0;
}
Hope this helps.
I have a very simple setup to try to test a staticLibrary:
I get error Syntax error: Identifier MyDataT, which points me to the function declaration in MyLib.h? Any reason why struct isn't recognized in the second solution?
Here is my pseudo code:
//MSGDefs.h ==> header file only contains struct defs like this:
typedef struct __msg {
unsigned long dest;
unsigned long src;
}MsgT
typedef struct __mydata : public MsgT
{
TimeT time;
DateT date;
}MyDataT;
//======== Library generates staic lib MyLib.lib, this generates library fine=======
//MyLib.h
#include MSGdefs.h
class X{
void process(MyDataT *data);
}
//MyLib.cpp
void X::process(MyDataT *data) { // do processing here ...}
//========================================
//MyTestLibProj.cpp -- Another Solution links to MyLib.lib
#include MyLib.h ==> This causes error of identifier MyDataT ???
int main(){
// X x = new X();
}
In main.C I am trying to access a nested structure member
(g.Fp1.d_status) and am getting the error:
'g.GridPt::Fp1' does not have class type
Below is my code. I can realy use some help. Thanks.
I tried stuff like g.MarchingDirection::Fp1.d_status,
g.&MarchingDirection::Fp1.d_status, ... with no luck.
//Filename: main.C
#include "GridPt.h"
int main()
{
GridPt g;
g.d_lstDistFn;
g.Fp1.d_status; \\ERROR: 'g.GridPt::Fp1' does not have class type
return 0;
}
//Filename: MarchingDirection.h
#ifndef included_MarchingDirection
#define included_MarchingDirection
struct MarchingDirection
{
MarchingDirection(double FValue);
double d_F;
int d_inOut;
char d_status;
};
#endif
//Filename: MarchingDirection.C
#include "MarchingDirection.h"
MarchingDirection::MarchingDirection(double FValue)
: d_F(FValue),
d_inOut(static_cast<int>(FValue)),
d_status('d'){}
//Filename: Grid2D.h
#ifndef included_GridPt
#define included_GridPt
#include "MarchingDirection.h"
#include <cstddef>
struct GridPt
{
GridPt();
double d_x, d_y;
MarchingDirection Fp1(double F=1.0), Fm1(double F=-1.0);
double d_lstDistFn;
};
#endif
//Filename: GridPt.C
#include "GridPt.h"
GridPt::GridPt()
: d_lstDistFn(0.0){}
You declared Fp1, Fm1 functions inside GridPt struct, they are not members
You could change to:
struct GridPt
{
GridPt();
double d_x, d_y;
MarchingDirection Fp1, Fm1;
double d_lstDistFn;
};
Initialize all your struct members shown as below:
GridPt::GridPt()
: d_x(0.0),
d_y(0.0),
Fp1(1.0),
Fm1(-1.0),
d_lstDistFn(0.0)
{}