Greetings! Today I wanted to show you a resource I have been working for a while: Class++
What is Class++?
Class++ is a module that aims to bring a class structure very similar to in languages such as C++ and Java. It brings access modifiers, function and operator overloading, inheritation, and many other concepts to Luau.
Why Class++?
It makes it so much easier to create classes, rather than focusing on figuring out how to use metatables just to be able to create a class system, you can just focus on what to add and overload in your class.
Usage Examples
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ClassPP = require(ReplicatedStorage["Class++"])
local class = ClassPP.class
local Person = class "Person" {
Public = {
Name = "",
Age = 0
}
}
local newPerson = Person.new()
print(newPerson.Age) -- Prints 0!
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ClassPP = require(ReplicatedStorage["Class++"])
local class = ClassPP.class
local Car = class "Car" {
Public = {
Brand = "Lamborghini",
},
Private = {
License_Plate = "XXXX"
}
}
local newCar = Car.new()
newCar.License_Plate = "YYYY" -- This will error!
Classic Way:
local Car = {}
Car.__index = Car
function Car.new()
local newcar = {}
setmetatable(newcar, Car)
newcar.Brand = "Lamborghini"
newcar.License_Plate = "XXXX"
return newcar
end
return Car
-------------------
local newCar = Car.new()
print(newCar.Brand)
To learn more about the usage and the syntax, you can visit the tutorial page.
API
Read the API
function classpp.class(className: string): (classData: classData) -> class
Creates a new class with the given classData table.
Parameters:
className: string
The class name for the new class.
classData: table
The classData table that contains the data about the new class.
Returns:
class
function classpp.getClass(className: string): class
Returns the class with the given className, if it exists.
Parameters:
className: string
The name of the desired class.
Returns:
class
To see all the methods and properties in the API, you can visit the API Reference
page.
This module is in Beta, it would be greatly appreciated if you could report any bugs you find on GitHub.
Not a bad implementation, but i will say it again and again. MiddleClass on github is the standard, and so far I’ve just not found anything better.
I took a look at your source, not bad but some of the code is not greatly comprehensible. Which is ok, you may be doing some really fancy things I am not so privy too. That being said, please comment the code, in many cases, someone might like your initial implementation but may need to tune it to cater to their needs and especially since Typed Luau is more advanced than most, it makes it considerably harder to understand what is going on.
That all being said, I will give it a look eventually down the line more practically.
One thing I have really noticed with many coders on this platform that are on the more skilled side that moved over to using VS coding and not coding directly with roblox, which is fine, tend to forget all the things that VS does automagically that roblox does not, and there are some people who don’t use that method of working to reduce issues. Just be aware of how you package and give out code and how you want to present it, the easier you make it the more people will want to use it. The more you make it readable and so even someone below your skill could come in and make edits or reasonable adjustments to fit their needs the better and more widespread it could be used.
If you are making things for others, think about the end user working with it.
Again, it looks pretty solid from my general review but just some tips on how you could get more widespread use on it.
Thank you! I was really looking forward to opinions like these.
Now to answer your concerns, if you’ve noticed in the init.luau file on the repo, I already tried adding some comments and even added the upcoming local documentation feature as a small test on top of the class method. So yes, more comments and explanations are coming soon in the source code.
Besides that, for the sole reason of explaining things better and making things easier for other developers, I’ve created a documentation page, so developers won’t have to figure out everything on their own, and I’ve pretty much documented everything in there.
Also, right now I’m working on making a model to upload to Creator Store, so developers can easily get the model without going through GitHub.
(Although it would be better for them in that case since updates and important changes are going to be documented there.)
Yup I hear you! And I will be open with you that I am biased when it comes to typed lua. I am not exactly sold on it just yet, but I will come around slowly I am sure.
And it’s good, def give middle-class a look though, its got literally every feature you could ask for out of a class implementation.
That being said I will keep my eye out and you can message me when i can review it better with comments there.
I’ve gave it a look, and it looks pretty nice, although my mission is to create an advanced class architecture with most of the features that are present in other languages.
For example, C++ was my main inspiration, the syntax is not hard to understand, it has many features such as function overloading, operator overloading, access specifiers, inheritation, and more. I even took some features from Java, like the final and abstract non-access specifiers. I took all of these features and decided to implement them in Luau, with the syntax very close to C++ so it’s easily understandable and easier to work with.
Originally, Class++ wasn’t written in full Typed Luau, but I decided to give it a shot, along with the native mode, and it significantly improved the performance. Especially with native, the interpreter gets used less, and the compiler does the job.
And good news, in the future I heard there will be a new type solver, which will improve the performance and the Typed Luau experience even further.
It will be interesting to see how it goes, atm typed and untyped don’t really do anything different from what I have read, but please point to the documentation if they have changed that recently!!
Typed lua is transpiled into base lua as of what I remember reading but perhaps that’s changed and that would be huge if they were doing that long term. Yea, native is really nice bit of a heavy memory cost but in highly computation heavy areas its Def worth the cost for sure!
For real, if they are skipping the transpiled aspect I would be really interested to know.
And here’s an image of a benchmarking test I’ve ran.
(Note: "Class++ Native & Strict is an older version of Class++, the performance is even better now with further optimizations)
Do you mean automatic typechecking for the class members? Trust me, I really tried. I don’t think it’s possible. Especially in this system where the classData properties are being transferred through several functions.
Though you have one solution: Since objects belong to the base type userdata, you can easily cast a custom type to them, and they will work perfectly fine!
Yes, this module aims to make OOP much easier for new and old developers!
I’m open to feedback by the way, make sure to send feature requests and bug reports if you have any!
Oh no, that would give an invalid access specifier error. Members must always be placed inside access specifiers. Try putting the function inside an access specifier, for example, Public like below:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ClassPP = require(ReplicatedStorage["Class++"])
local class = ClassPP.class
local Car = class "Car" {
Public = {
Brand = "Lamborghini",
changeLicensePlate = function(self, plate: string) -- We put this function inside the Public Access Specifier, so it can be accessible from everywhere.
self.License_Plate = plate
end,
},
Private = {
License_Plate = "XXXX"
},
}
local newCar = Car.new()
newCar:changeLicensePlate("YYYY")
Check out the documentation for more information access specifiers! You should read the entire thing through, it would be very useful.
Improved performance on accessing an object’s Private and Protected access specifiers.
Updated the final and abstract functions to now accept multiple classes with a new syntax.
Util.inClassScope function now accepts a new optional defaultLevel value that determines the default call stack level the function will start doing the checks on.
A performance comparison picture between 1.1.0 and 1.2.0:
Classes can now have static members using this function, just like in Java and C++, they belong to the class, and will not replicate to the objects created from the class.
They’re global, and will be the same everywhere.
You can access the static members through the class object like: class.<memberName>.property or class.<memberName>.p for short.