C++ global single pass frustrations
Posted: Tue Feb 23, 2021 10:29 am
This has annoyed me for a while. In C++ it's not possible to do this:
I can't declare "class Cat;" at the top, because TransformIntoACat() returns Cat as a value.
C++ doesn't have partial classes, so I can't add "TransformIntoACat" after both Cat and Dog is defined.
I could return a pointer, but I'd have to allocate memory for Cat/Dog.
As an alternative I could built a transformer functions:
Or, take a reference to Cat or Dog as a constructor arguments.
If C++ wasn't single pass (outside of classes/structs), it could see that Cat/Dog was defined later.
This is frustrating because I'm building an IDL->C++ code generator, and references to objects are passed around as {buffer, offset}, and types can reference each other in my IDL, e.g.: An Expression object can contain an Addition operator, which can contain 2 Expression operators, and in an ideal world, this would generate code such as:
Because it would be nice to be able to do: expression.GetAdditionOperation().GetSideA();
But, instead I have to fake it with:
Which at least gets me: expression->GetAdditionOperation()->GetSideA();
But, ducktyping Ref<T> into T seems "wrong" although it works.
I know C++ uses a multi-pass compiler because you don't need to use forward declarations inside of classes and structs, but it's frustrating that for declarations outside of a class it's acts like a single pass compiler.
[/rant]
Code: Select all
class Dog {
Cat TransformIntoACat();
};
class Cat {
Dog TransformIntoADog();
};
C++ doesn't have partial classes, so I can't add "TransformIntoACat" after both Cat and Dog is defined.
I could return a pointer, but I'd have to allocate memory for Cat/Dog.
As an alternative I could built a transformer functions:
Code: Select all
Cat ConvertDogToCat(const Dog& dog);
Dog ConvertCatToDog(const Cat& cat);
If C++ wasn't single pass (outside of classes/structs), it could see that Cat/Dog was defined later.
This is frustrating because I'm building an IDL->C++ code generator, and references to objects are passed around as {buffer, offset}, and types can reference each other in my IDL, e.g.: An Expression object can contain an Addition operator, which can contain 2 Expression operators, and in an ideal world, this would generate code such as:
Code: Select all
class Expression {
public:
AdditionOperation GetAdditionOperation();
private:
Buffer* buffer;
size_t offset;
};
class AdditionOperation {
public:
Expression GetSideA();
Expression GetSideB();
private:
Buffer* buffer;
size_t offset;
};
But, instead I have to fake it with:
Code: Select all
template <class T>
code Ref<T> {
public:
T* operator::->() {
return static_cast<T*>(this);
}
private:
Buffer* buffer;
size_t offset;
}
class AdditionOperation;
class Expression {
public:
Ref<AdditionOperation> GetAdditionOperation();
private:
Buffer* buffer;
size_t offset;
};
class AdditionOperation {
public:
Ref<Expression> GetSideA();
Ref<Expression> GetSideB();
private:
Buffer* buffer;
size_t offset;
};
But, ducktyping Ref<T> into T seems "wrong" although it works.
I know C++ uses a multi-pass compiler because you don't need to use forward declarations inside of classes and structs, but it's frustrating that for declarations outside of a class it's acts like a single pass compiler.
[/rant]