ACCU World of Code
-
Elm makes me happy (updated for Elm 0.19) video
Series: Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test, Elm JSON With up-to-date examples, and anecdotes from the last couple of years of continuing to enjoy writing web pages in Elm, here’s a new version of my Elm advert: Slides: Elm ma…
-
Godot: make new objects at runtime (instancing) video
Series: 2D Shapes, drag and drop, new objects My Godot 3 game is progressing, and I am starting to think I am actually writing the level editor. Here’s how I wrote code to make new versions of existing objects (by converting them to scenes): Godot ve…
-
You must rewind your incoming buffer when you fail to encode a character in a CharsetEncoder or you’ll get an IllegalArgumentException
I am writing a CharsetEncoder in Java, which is my kind of fun. I was getting a mysterious error when I identified that I could not encode certain characters: Exception in thread “main” java.lang.IllegalArgumentException at java.nio.Buffer.position(Bu…
-
Godot: Dragging and dropping physics objects video
Series: 2D Shapes, drag and drop, new objects Continuing to explore the Godot 3 game engine. I want to make a game where you drag blocks around and balance them on each other, but I couldn’t find much documentation on how to drag-and-drop objects (exce…
-
Godot: 2D shapes bouncing off each other video
Series: 2D Shapes, drag and drop, new objects In which I make a square and a triangle bounce on a rectangle using Godot 3: Godot version: v3.0.6.stable.official.8314054
-
PC-lint Plus and Gimpel’s new website
Gimpel Software (the vendor behind PC-lint and PC-lint Plus) <have recently updated their website, and it is now dedicated entirely to PC-lint Plus. If you are considering upgrading from PC-lint 9.0 to PC-lint Plus, the relevant information is avail…
-
PC-lint Plus and Gimpel’s new website
Gimpel Software (the vendor behind PC-lint and PC-lint Plus) <have recently updated their website, and it is now dedicated entirely to PC-lint Plus. If you are considering upgrading from PC-lint 9.0 to PC-lint Plus, the relevant information is avail…
-
LintProject Pro End of Life Notice
LintProject Pro is a command line only product which can perform a basic per-file analysis of a C/C++ codebase using PC-lint or CppCheck. In many ways it was the proof of concept for Visual Lint, and although it has served us well, it’s getting a bit l…
-
LintProject Pro End of Life Notice
LintProject Pro is a command line only product which can perform a basic per-file analysis of a C/C++ codebase using PC-lint or CppCheck. In many ways it was the proof of concept for Visual Lint, and although it has served us well, it’s getting a bit l…
-
Elm JSON decoder examples
Series: Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test, Elm JSON I find JSON decoding in Elm confusing, so here are some thoughts and examples. Setup $ elm –version 0.19.0 $ mkdir myproj; cd myproj $ elm init … $ elm install elm/json …..
-
static_assert in templates
Quiz time! Which of the following programs can you expect to not compile? For bonus points, which are required by the C++ standard to not compile? Program 1 Program 2 Program 3 In case you’re not familiar with static_assert, it takes a constant boolean expression, and if it evaluates to false, you get a compilation … Continue reading static_assert in templates
-
+!!””
In this Tweet, @willkirkby posts: +!!”” that evaluates as 1 in C/C++, but no, JavaScript is the weird language C++ is indeed weird, or at least it’s very weakly typed. Let’s go through all the details of what’s going on here: Summary: Starting from the right, “” is a string literal, which gets converted to … Continue reading +!!””
-
Graft Animation Language on Raspberry Pi
Because the Rapsberry Pi uses a slightly older Python version, there is a special version of Graft for it. Here’s how to get it: Open a terminal window by clicking the black icon with a “>” symbol on it at the top near the left. First we need to in…
-
Disaster Recovery: A Dynamic Redundancy Approach
The problem with disaster planning is that it is not rehearsed. When you need to retrieve a file from backup is when you discover that your backup has been broken for three months. Modern cloud systems, based upon software defined infrastructure and…
-
Disaster Recovery: A Dynamic Redundancy Approach
The problem with disaster planning is that it is not rehearsed. When you need to retrieve a file from backup is when you discover that your backup has been broken for three months. Modern cloud systems, based upon software defined infrastructure and…
-
Why you can’t list-initialize containers of non-copyable types
Have you ever wondered why you can’t list-initialize containers of non-copyable types? This is for instance not possible: If you ever wondered, or if you now are, read on! List-initialization Since C++11, you’re probably used to intitalizing containers like this: This of course also works with user defined types. Let’s say you have a class … Continue reading Why you can’t list-initialize containers of non-copyable types
-
Worksheet: “Tell a story by making animations with code”
I’m running a workshop at the Egham Raspberry Jam on 21st October. The workshop will introduce my little animation language Graft. We will tell a story using animations that we created ourselves using code. The worksheet for the workshop is here: PDF o…
-
Direct access to SonarQube Postgresql Database
I want to change to change the name of a sonarqube project. This cannot be done without performing another analysis. You can just do it in SQL https://stackoverflow.com/questions/30511849/how-to-rename-a-project-in-sonarqube-5-1 but you have to be…
-
Direct access to SonarQube Postgresql Database
I want to change to change the name of a sonarqube project. This cannot be done without performing another analysis. You can just do it in SQL https://stackoverflow.com/questions/30511849/how-to-rename-a-project-in-sonarqube-5-1 but you have to be…
-
My experience upgrading to Elm 0.19
Elm is unstable, so upgrading to the next version can be painful. Here’s what I needed to do to upgrade from 0.18 to 0.19. Replace elm-package.json and tests/elm-package.json with elm.json – e06f5a1728 Switch to the new elm-test – b964b7c7a Re-arrange…
-
Bulk adding items to Wunderlist using wunderline on Ubuntu MATE
If you use Wunderlist and want to be able to bulk-add tasks from a text file, first install and set up wunderline. Now, to be able to right-click a text file containing one task per line on Ubuntu MATE, create a file called “wunderlist-bulk-add” in ~/….
-
Writing a new Flarum extension on Ubuntu
In a previous post I described how to install Flarum locally on Ubuntu. Here is how I set up my development environment on top of that setup so I was able to write a new Flarum extension and test it on my local machine. Recap: I installed Apache and PH…
-
Ubuntu “compose” key for easy unicode character input
I found out on Mastodon recently that the Compose key exists, allowing you to enter special characters using easy-to-remember key sequences (e.g. “<compose>/=” gives you “≠“). To do this on Ubuntu (and probably many other systems), hold SHIFT, …
-
Redirecting all requests to https and www using .htaccess in Apache
I want all requests to artificialworlds.net/rabbit-escape/levels/ to get redirected to use the https protocol, and to include “www.” at the beginning of the URL, and I found lots of Stack Overflow articles, but nothing there worked perfectly for me. He…
-
Example of a systemd service file
Here is an almost-minimal example of a systemd service file, that I use to run the Mastodon bot of my generative art playground Graft. I made a dedicated user just to run this service, and installed Graft into /home/graft/apps/graft under that username…
-
Spaceship Operator
You write a class. It has a bunch of member data. At some point, you realise that you need to be able to compare objects of this type. You sigh and resign yourself to writing six operator overloads for every type of comparison you need to make. Afterwa…
-
Installing Flarum on Ubuntu 18.04
I am setting up a forum for sharing levels for my game Rabbit Escape, and I have decided to try and use Flarum, because it looks really usable and responsive, has features we need like liking posts and following authors, and I think it will be reasonab…
-
Rabbit Escape 0.11 out now!
The RABBOTS are coming! Get the latest version of Rabbit Escape: Android: Rabbit Escape on F-Droid (free, no ads) Android: Rabbit Escape Free on the Play Store (free, no ads) Android: Rabbit Escape on the Play Store (Pay about 60p, identical to the f…
-
Migrating videos from YouTube to PeerTube inside a Docker container
I have quite a few videos hosted on YouTube that I would like to upload to my new PeerTube location, but I don’t want to install all the PeerTube dependencies on my machine, so I did it all inside a Docker image. First I built and started a Docker cont…
-
How to write a programming language articles
Recent Overload journal issues contain my new articles on How to Write a Programming Language. Part 1: How to Write a Programming Language: Part 1, The Lexer Part 2: How to Write a Programming Language: Part 2, The Parser PDF of the latest issue: Overl…
-
Roundup running on Python 3
Following last week’s upgrade of my Roundup instance to 1.6.0, I have now applied the Python 3 patches from issue2550960 with a few more fixes available in my py3-cmeerw branch and now have both wg21.cmeerw.net as well as issues.cmeerw.org working with…
-
Roundup running on Python 3
Following last week’s upgrade of my Roundup instance to 1.6.0, I have now applied the Python 3 patches from issue2550960 with a few more fixes available in my py3-cmeerw branch and now have both wg21.cmeerw.net as well as issues.cmeerw.org working with…
-
Roundup Updated for wg21.cmeerw.net
wg21.cmeerw.net got a major update with Roundup being updated to 1.6.0 and the template converted to being based on jinja2 which should make it more friendly to mobile devices.
-
Roundup Updated for wg21.cmeerw.net
wg21.cmeerw.net got a major update with Roundup being updated to 1.6.0 and the template converted to being based on jinja2 which should make it more friendly to mobile devices.
-
Allow drag-to-side, but not drag-to-top in Ubuntu MATE (Marco)
I love the “tiling” feature in many window managers including Marco that means I can drag windows to the side of the screen and get them covering one half. However, I never use the similar feature that allows dragging a window to the top, and it often …
-
Super Simple Named Boolean Parameters
Quite often we come across interfaces with multiple boolean parameters, like this: cake make_cake (bool with_dairy, bool chocolate_sauce, bool poison); A call to this function might look like: auto c = make_cake(true, true, false); Unfortunately,…
-
Connecting to Slack from an IRC client using slirc
I tried to get back to an IRC interface to Slack using Matrix, and it had some problems. Thanks to Colin Watson’s comment on that post, I tried Daniel Beer’s slirc, and so far it seems to be working pretty well. Here’s what I did: Get a Slack legacy to…
-
Using Matrix to connect to Slack from an IRC client on Ubuntu
Update: I found a better solution using slirc. I like using HexChat to talk to my colleagues, like one other guy. It is fast, and it pops up a new window when someone sends me a direct/private message. Recently, Slack shut down their IRC gateway, forci…
-
Function Template Partial Ordering: Worked Examples
C++ function overloading rules are complex. C++ template rules are complex. Put the two together, and you unfortunately do not get something simple; you get a hideous monster of standardese which requires great patience and knowledge to overcome. Howev…
-
Shooting with Flash (And Motofest 2018)
Talking about shooting with an on-camera flash at Coventry Motofest 2018.I’ve never really used my flashgun before, mostly because on the odd occasion that I have used it at a shoot I ended up with under, or over exposed images and thus never wanted to…
-
Clever Things People Do In Groovy So You Have To Know About Them video
Groovy has lots of interesting syntax that can be used for domain-specific languages, such as Gradle build files, and Jenkinsfiles. I try to demystify the syntax tricks a bit so you have a chance to read and understand what the code is actually doing: …
-
Gitlab certificates
On Ubuntu, cloning a repo from a machine you don’t have a certificate for will give the error: fatal: unable to access ‘https://servername’: server certificate verification failed. CAFuile /etc/ssl/certs/your_filename CRLfile: None You can …
-
Gitlab certificates
On Ubuntu, cloning a repo from a machine you don’t have a certificate for will give the error: fatal: unable to access ‘https://servername’: server certificate verification failed. CAFuile /etc/ssl/certs/your_filename CRLfile: None You can …
-
std::accumulate vs. std::reduce
std::accumulate has been a part of the standard library since C++98. It provides a way to fold a binary operation (such as addition) over an iterator range, resulting in a single value. std::reduce was added in C++17 and looks remarkably similar. This …
-
Perforce Acquires PRQA
Clearlake Capital-Backed Perforce Adds Enterprise-Grade Source Code Analysis to its Portfolio With Acquisition of PRQA
-
Perforce Acquires PRQA
Clearlake Capital-Backed Perforce Adds Enterprise-Grade Source Code Analysis to its Portfolio With Acquisition of PRQA
-
ACCU Conference 2018
We had an absolute blast at this year’s ACCU Conference and if you were there we imagine you did too. For us the highlight had to be the launch of #include <C++>, a new global, inclusive, and diverse community for developers interested in C++. …
-
ACCU Conference 2018
We had an absolute blast at this year’s ACCU Conference and if you were there we imagine you did too. For us the highlight had to be the launch of #include <C++>, a new global, inclusive, and diverse community for developers interested in C++. …
-
Examples of SQL join types (LEFT JOIN, INNER JOIN etc.)
I have 2 tables like this: > SELECT * FROM table_a; +——+——+ | id | name | +——+——+ | 1 | row1 | | 2 | row2 | +——+——+ > SELECT * FROM table_b; +——+——+——+ | id | name | aid | +——+——+——+ | …
-
Custom Alias Analysis in LLVM
At Codeplay I currently work on a compiler backend for an embedded accelerator. The backend is the part of the compiler which takes some representation of the source code and translates it to machine code. In my case I’m working with LLVM, so this repr…
-
East End Functions
There has been a recent stirring of attention, in the C++ community, for the practice of always placing the const modifier to the right of the thing it modifies. The practice has even been gifted a catchy name: East Const (which, I think, is what has…
-
East End Functions
There has been a recent stirring of attention, in the C++ community, for the practice of always placing the const modifier to the right of the thing it modifies. The practice has even been gifted a catchy name: East Const (which, I think, is what has…
-
No more pointers
This was an April Fools post. One of the major changes at the most recent C++ standards meeting in Jacksonville was the decision to deprecate raw pointers in C++20, moving to remove them completely in C++23. This came as a surprise to many, with a lot…
-
emBO++ 2018 Trip Report
emBO++ is a conference focused on C++ on embedded systems in Bochum, Germany. This was it’s second year of operation, but the first that I’ve been along to. It was a great conference, so I’m writing a short report to hopefully convince more of you to a…
-
ACME DNS Validation
I was looking at modifying acme tiny to support DNS-01 validation with a custom PowerDNS backend just a few days ago (in my case to get certificates for an XMPP server where there isn’t a corresponding HTTP server or the HTTP server is hosted on a diff…
-
ACME DNS Validation
I was looking at modifying acme tiny to support DNS-01 validation with a custom PowerDNS backend just a few days ago (in my case to get certificates for an XMPP server where there isn’t a corresponding HTTP server or the HTTP server is hosted on a diff…
-
Fixing Slack emojis in HexChat
If, like me, you are this person: (Source: xkcd.com/1782) You may want to fix the stupid :slightly_smiling_face: messages you receive from Slack via the IRC gateway. Obviously, I’d prefer they went away entirely, but it’s still better to see a characte…
-
Ideas on how lexing will work in Pepper3
I am trying to practice documentation-driven development in Pepper3, so every time I start on an area, I will write documentation explaining how it works, and include examples that are automatically verified during the build. I’ve started work on lexin…
-
Deleting commits from the git history
Today I wanted to fix a Git repo that contained some bad commits (i.e. git fsck complained about them). [I wanted to do this because GitLab was not allowing me to push the bad commits.] I wanted the code to look exactly as it did before, but the histor…
-
Do compilers take inline as a hint?
If you’ve spent any time in C or C++ communities online, you’ve probably seen someone say this: inline used to be a hint for compilers to inline the definition, but no compilers actually take that into account any more. You shouldn’t believe ever…
-
Questions and answers about Pepper3
Series: Examples, Questions My last post Examples of Pepper3 code was a reply to my friend’s email asking what it was all about. They replied with some questions, and I thought the questions and answers might shed some more light: Questions! Brillian…
-
Examples of Pepper3 code
Series: Examples, Questions I have restarted my effort to make a new programming language that fits the way I like things. I haven’t pushed any code yet, but I have made a lot of progress in my head to understand what I want. Here are some random examp…
-
Passing overload sets to functions
Passing functions to functions is becoming increasingly prevalent in C++. With common advice being to prefer algorithms to loops, new library features like std::visit, lambdas being incrementally beefed up12 and C++ function programming talks consisten…
-
Constantly Confusing: C++ const and constexpr pointer behaviour
A quick explanation of how const and constexpr work on pointers in C++So I was checking that my knowledge was correct when working on a Firefox bug.I made a quick C++ file with all the examples I know of how to use const and constexpr on pointers.As on…
-
Working with PDF Highlight Annotations Programmatically
PDFs are the format of choice in academia, but extracting the information they contain is annoyingly hard.I’ve just started working on my degree’s final project. An academic project requires lots of research, which means reading lots of papers.Papers a…
-
Recording gameplay videos on RetroPie
Stop! This blog post is out of date – see Recording gameplay videos on RetroPie for a live version in github that may be more helpful. Credits: this is a slightly corrected and shortened version of How To Record A GamePlay Video From A RetroPie by sels…
-
Resizable UIWindow on iOS
I came across a Todo item in my inbox (I often email myself things todo) whilst tidying it up following Christmas, entitled “Make a resizable UIWindow”. I can remember sending this to myself but not what I was reading or watching that prompted me to do…
-
Resizable UIWindow on iOS
I came across a Todo item in my inbox (I often email myself things todo) whilst tidying it up following Christmas, entitled “Make a resizable UIWindow”. I can remember sending this to myself but not what I was reading or watching that prompted me to do…
-
I don’t have hope
I don’t have hope about the state of society regarding support of trans people.On Trans Day of Remembrance 2017 I organised and spoke at Coventry Pride’s event.I delivered the following words to the attendees. Since I spent so much time crying over wri…
-
The World’s First Distributed C++ Meet-up (*)
(* Probably) Last week my London based C++ user group, C++ London, joined forces with SwedenCpp, based in Stockholm, for a distributed event where we shared video streams with each other. The whole thing was hosted by King, who took care of the audio…
-
The World’s First Distributed C++ Meet-up (*)
(* Probably) Last week my London based C++ user group, C++ London, joined forces with SwedenCpp, based in Stockholm, for a distributed event where we shared video streams with each other. The whole thing was hosted by King, who took care of the audio…
-
Catch2 Released
I’ve been talking about Catch2 for a while – but now it’s finally here The big news for Catch2 is that it drops all support for pre-C++11 compilers. Other than meaning that some users will not be supported (you can still use Catch “Classic” (1.x) – …
-
Catch2 Released
I’ve been talking about Catch2 for a while – but now it’s finally here The big news for Catch2 is that it drops all support for pre-C++11 compilers. Other than meaning that some users will not be supported (you can still use Catch “Classic” (1.x) – …
-
I think I’ve worked out why Uni is so stressful.
The study environment generates panic.I’m back at University after taking a year out to work as a professional software developer.I got back and instantly found everything so incredibly stressful.Moreso than I felt before during my earlier years at Uni…
-
How to read water
is an excellent book by Tristan Gooley (isbn 978-1-473-61522-9). As usual I’m going to quote from a few pages: One of the universal truths of human observation is that we see more of what we expect to see and less of what we don’t expect to see…
-
How to read water
is an excellent book by Tristan Gooley (isbn 978-1-473-61522-9). As usual I’m going to quote from a few pages: One of the universal truths of human observation is that we see more of what we expect to see and less of what we don’t expect to see…
-
Organize for Complexity
is an excellent book by Niels Pflaeging (isbn 978-0-9915376-0-0). As usual I’m going to quote from a few pages: What Taylor pioneered was the idea of consistently dividing an organization between thinking people (managers) and executing people (w…
-
Organize for Complexity
is an excellent book by Niels Pflaeging (isbn 978-0-9915376-0-0). As usual I’m going to quote from a few pages: What Taylor pioneered was the idea of consistently dividing an organization between thinking people (managers) and executing people (w…
-
TECH(K)NOW Day workshop on “Writing a programming language”
My OpenMarket colleagues and I ran a workshop at TECH(K)NOW Day on how to write your own programming language: A big thank you to my colleagues from OpenMarket who volunteered to help: Rowan, Jenny, Zach, James and Elliot. An extra thank you to Zach …
-
FileZilla with proxy not working with strange characters in the password
Today I found I could not connect out through my proxy server with FileZilla It stopped working when I changed my password to something containing a double quote “. The solution? Change to use WinSCP.
-
HTML5 CSS Toolbar + zoomable workspace that is mobile-friendly and adaptive
I have been working on a prototype level editor for Rabbit Escape, and I’ve had trouble getting the layout I wanted: a toolbar at the side or top of the screen, and the rest a zoomable workspace. Something like this is very common in many desktop appli…
-
maven-assembly-plugin descriptor for a simple tarball with dependencies
Today I was trying to make a simple tarball of a project + its dependent jar using the maven-assembly-plugin. I know this is a terrible way to do anything, but hey, just in case someone else wants to do something just as terrible, here are my pom.xml a…
-
Blog aggregator/planet in WordPress using FeedWordPress
I used to run Planet Code using Venus but that started breaking recently with 500 errors for SNI-enabled sites, and it looks very unmaintained. I considered some other static aggregators, but on shared hosting it’s hard to get the right Ruby version an…
-
The book of five rings
is an excellent book by Miyamoto Musashi, translated by Thomas Cleary (isbn 1-57062-748-7). As usual I’m going to quote from a few pages: Preface: In common parlance, to do something with a real sword means to do it with utmost earnestness… The Bo…
-
The book of five rings
is an excellent book by Miyamoto Musashi, translated by Thomas Cleary (isbn 1-57062-748-7). As usual I’m going to quote from a few pages: Preface: In common parlance, to do something with a real sword means to do it with utmost earnestness… The Bo…
-
Unit Testing with Mocha, a local instance of dynamoDB & Promises
I’m writing the backend for my current iOS App in Javascript using node.js, AWS Lambda along with DynamoDB. My AWS Lambda code is mostly AWS Lambda agnostic except for the initial handler methods, this makes them fairly testable outside of AWS. Howeve…
-
Unit Testing with Mocha, a local instance of dynamoDB & Promises
I’m writing the backend for my current iOS App in Javascript using node.js, AWS Lambda along with DynamoDB. My AWS Lambda code is mostly AWS Lambda agnostic except for the initial handler methods, this makes them fairly testable outside of AWS. Howeve…
-
Adding a day in Python datetimes – use timedelta, not the datetime constructor
If you want “tomorrow” in Python datetimes, don’t construct a datetime like this: from datetime import datetime, timedelta td = datetime.today() tm1 = datetime(td.year, td.month, td.day + 1, 14, 0, 0) # Don’t do this! Because it will work sometimes, b…
-
practising jQuery in cyber-dojo
cyber-dojo now supports practising jQuery. Enjoy 🙂
-
practising jQuery in cyber-dojo
cyber-dojo now supports practising jQuery. Enjoy 🙂
-
Thames path improvements – Letter to Oxfordshire County Council
I have sent the following letter to LTS.Team AT oxfordshire.gov.uk in response to https://consultations.oxfordshire.gov.uk/consult.ti/OxfordRiversideRoutes/.   Hi,   I have submitted the following via the questionnaire:   The Oxpe…
-
Thames path improvements – Letter to Oxfordshire County Council
I have sent the following letter to LTS.Team AT oxfordshire.gov.uk in response to https://consultations.oxfordshire.gov.uk/consult.ti/OxfordRiversideRoutes/.   Hi,   I have submitted the following via the questionnaire:   The Oxpe…
-
Broken Levels Challenge – Egham Raspberry Pi Jam July 2017
Today at the Egham Raspberry Pi Jam we did two things: 1. The Broken Levels Challenge Some nasty person came and broke our levels for our game Rabbit Escape and we need you to fix them! To play this game you will need a PC version of Rabbit Escape, ou…
-
Pride Vibes 2017: Isle of Wight Pride
Isle of Wight Pride welcomed people of all ages to a fantastic eventPride Vibes: As a photographer for Gay Pride Pics, I attend lots of Prides across the UK every year. Each Pride has a different feel. This series will describe what each Pride was like…
-
On Company Diversity Targets
A diversity target number isn't all it might seem to be.Diversity targets show you’re dedicated to giving women a big enough boxThis a reply to Carol Roth’s piece for Entrepreneur. Please read that piece before this one. Make sure to form your own …
-
compile time assertions in C
C has a facility for checking dynamic assertions at run-time. It’s inside <assert.h> and its called assert. Now assert is a macro, so why isn’t it called ASSERT? I don’t know. Prior art no doubt. Anyway, assert is a dynamic runtime feature, you can only use it inside functions.
/* not inside a function, won't compile :-( */ assert(sizeof(int) * CHAR_BIT >= 32);
That’s a pity because it would be nice if I could get the compiler to check things like this automatically at compile time. I’ve occasionally seen an attempt at a compile-time check like this…
#if sizeof(int) * CHAR_BIT < 32 #error People of Earth. Your attention please... #endif
But this doesn’t work. The C preprocessor is a glorified text reformatter: it knows practically nothing about C. However, there is a way to write this as a compile time assertion (and moving any error trap to an earlier phase is a Good Thing)
{ yourself }
#define COMPILE_TIME_ASSERT(expr) \ char constraint[expr] COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
What’s is going on here? Well, the example preprocesses to…
char constraint[sizeof(int) * CHAR_BIT >= 32];
If the expression is true, (an int is at least 32 bits), the expression will have a value of one, and constraint will be an array of one char. If the assertion is false, (an int is less than 32 bits), the expression will have a value of zero, and constraint will be an empty array. That’s illegal, and you’ll get a compile time error. Viola, a compile time assertion 🙂 You can use it inside and outside a function but you can’t use it twice in the same function, as you end up with a duplicate definition. To solve that problem you could resort to some convoluted macro trickery:
#define COMPILE_TIME_ASSERT(expr) char UNIQUE_NAME[expr] #define UNIQUE_NAME MAKE_NAME(__LINE__) #define MAKE_NAME(line) MAKE_NAME2(line) #define MAKE_NAME2(line) constraint_ ## line
But this is pretty horrible. Also, you will probably get warnings about unused variables. Take a step back for a moment and think about why it works at all. It’s because you have to specify the size of an array as a compile time constant. The formal grammar of a direct-declarator tells you this. Let’s look at some bits of grammar more closely:
Constrained arrays
direct-declarator: identifier ( declarator ) direct-declarator [ constant-expression opt ] direct-declarator ( parameter-type-list ) direct-declarator ( identifier-list opt )
I just piggy backed on this, using the constraint that the value of the constant expression cannot (in this context) be zero. A natural question (to the curious) is are there other parts of the formal grammar that require a constant expression. The answer, of course, is yes.
Constrained enums
enumerator: enumeration-constant enumeration-constant = constant-expression
However, I can’t use this because there are no useful constraints in this context.
Constrained bit-fields
struct-declarator: declarator declarator opt : constant-expression
Reading the constraints of a bit field I see that if the width of a bit-field is zero the declaration cannot have a declarator. In other words this is legal…
struct x { unsigned int : 0; };
but this is not…
struct x { unsigned int bf : 0; };
This suggests another way to create a compile time assertion
#define COMPILE_TIME_ASSERT(expr) \ struct x { unsigned int bf : expr; } COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
Trying this we again get duplicate definitions, not of a variable this time, but of the type struct x. However we can fix this by creating an anonymous struct:
#define COMPILE_TIME_ASSERT(expr) \ struct { unsigned int bf : expr; }
This works. However, now you’ll probably get warnings about the unused untagged struct.
There is one last bit of grammar that uses a constant-expression.Constrained switch
labelled-statement: identifier : statement case constant-expression : statement default : statement
It’s well known that you can’t have two case labels with the same constant. The following will not compile…
switch (0) { case 0: case 0:; }
So, here’s yet another way to create a compile time assertion. This time we don’t create a dummy variable, or a dummy type, but a dummy statement. A dummy switch statement:#define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
If pred evaluates to true (i.e., 1) then the case labels will be 0 and 1. Different; Ok. If pred evaluates to false (i.e., 0) then the case labels will be 0 and 0. The same; Compile time error. Viola. However, a switch statement cannot exist in the global scope. So the last piece of the puzzle is to put the compile time assertions inside a function.
#include <limits.h> #define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} #define ASSERT_MIN_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT >= size) #define ASSERT_EXACT_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT == size) void compile_time_assertions(void) { ASSERT_MIN_BITSIZE(char, 8); ASSERT_MIN_BITSIZE(int, 16); ASSERT_EXACT_BITSIZE(long, 32); }
-
compile time assertions in C
C has a facility for checking dynamic assertions at run-time. It’s inside <assert.h> and its called assert. Now assert is a macro, so why isn’t it called ASSERT? I don’t know. Prior art no doubt. Anyway, assert is a dynamic runtime feature, you can only use it inside functions.
/* not inside a function, won't compile :-( */ assert(sizeof(int) * CHAR_BIT >= 32);
That’s a pity because it would be nice if I could get the compiler to check things like this automatically at compile time. I’ve occasionally seen an attempt at a compile-time check like this…
#if sizeof(int) * CHAR_BIT < 32 #error People of Earth. Your attention please... #endif
But this doesn’t work. The C preprocessor is a glorified text reformatter: it knows practically nothing about C. However, there is a way to write this as a compile time assertion (and moving any error trap to an earlier phase is a Good Thing)
{ yourself }
#define COMPILE_TIME_ASSERT(expr) \ char constraint[expr] COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
What’s is going on here? Well, the example preprocesses to…
char constraint[sizeof(int) * CHAR_BIT >= 32];
If the expression is true, (an int is at least 32 bits), the expression will have a value of one, and constraint will be an array of one char. If the assertion is false, (an int is less than 32 bits), the expression will have a value of zero, and constraint will be an empty array. That’s illegal, and you’ll get a compile time error. Viola, a compile time assertion 🙂 You can use it inside and outside a function but you can’t use it twice in the same function, as you end up with a duplicate definition. To solve that problem you could resort to some convoluted macro trickery:
#define COMPILE_TIME_ASSERT(expr) char UNIQUE_NAME[expr] #define UNIQUE_NAME MAKE_NAME(__LINE__) #define MAKE_NAME(line) MAKE_NAME2(line) #define MAKE_NAME2(line) constraint_ ## line
But this is pretty horrible. Also, you will probably get warnings about unused variables. Take a step back for a moment and think about why it works at all. It’s because you have to specify the size of an array as a compile time constant. The formal grammar of a direct-declarator tells you this. Let’s look at some bits of grammar more closely:
Constrained arrays
direct-declarator: identifier ( declarator ) direct-declarator [ constant-expression opt ] direct-declarator ( parameter-type-list ) direct-declarator ( identifier-list opt )
I just piggy backed on this, using the constraint that the value of the constant expression cannot (in this context) be zero. A natural question (to the curious) is are there other parts of the formal grammar that require a constant expression. The answer, of course, is yes.
Constrained enums
enumerator: enumeration-constant enumeration-constant = constant-expression
However, I can’t use this because there are no useful constraints in this context.
Constrained bit-fields
struct-declarator: declarator declarator opt : constant-expression
Reading the constraints of a bit field I see that if the width of a bit-field is zero the declaration cannot have a declarator. In other words this is legal…
struct x { unsigned int : 0; };
but this is not…
struct x { unsigned int bf : 0; };
This suggests another way to create a compile time assertion
#define COMPILE_TIME_ASSERT(expr) \ struct x { unsigned int bf : expr; } COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
Trying this we again get duplicate definitions, not of a variable this time, but of the type struct x. However we can fix this by creating an anonymous struct:
#define COMPILE_TIME_ASSERT(expr) \ struct { unsigned int bf : expr; }
This works. However, now you’ll probably get warnings about the unused untagged struct.
There is one last bit of grammar that uses a constant-expression.Constrained switch
labelled-statement: identifier : statement case constant-expression : statement default : statement
It’s well known that you can’t have two case labels with the same constant. The following will not compile…
switch (0) { case 0: case 0:; }
So, here’s yet another way to create a compile time assertion. This time we don’t create a dummy variable, or a dummy type, but a dummy statement. A dummy switch statement:#define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} COMPILE_TIME_ASSERT(sizeof(int) * CHAR_BIT >= 32);
If pred evaluates to true (i.e., 1) then the case labels will be 0 and 1. Different; Ok. If pred evaluates to false (i.e., 0) then the case labels will be 0 and 0. The same; Compile time error. Viola. However, a switch statement cannot exist in the global scope. So the last piece of the puzzle is to put the compile time assertions inside a function.
#include <limits.h> #define COMPILE_TIME_ASSERT(pred) \ switch(0){case 0:case pred:;} #define ASSERT_MIN_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT >= size) #define ASSERT_EXACT_BITSIZE(type, size) \ COMPILE_TIME_ASSERT(sizeof(type) * CHAR_BIT == size) void compile_time_assertions(void) { ASSERT_MIN_BITSIZE(char, 8); ASSERT_MIN_BITSIZE(int, 16); ASSERT_EXACT_BITSIZE(long, 32); }
-
C# struct/class differences
- Events are locked?
- Exist on stack or heap?
- Can cause garbage collection?
- Meaning of this?
- Always has a default constructor?
- Default construction triggers static construction?
- Can be null?
- Use with the as operator?
- Can be locked?
- Can have a destructor?
- Default field layout?
- Can be a volatile field?
- Can have synchronized methods?
- Can be pointed to?
- Can be stackalloc’d?
- Can be sizeof’d?
- How to initialize fields?
- Inheritance differences?
- Equals behavior
Events are locked?
Events declared in a class have their += and -= access automatically locked via a lock(this) to make them thread safe (static events are locked on the typeof the class). Events declared in a struct do not have their += and -= access automatically locked. A lock(this) for a struct would not work since you can only lock on a reference type expression.
Exist on stack or heap?
Value type local instances are allocated on the stack. Reference type local instances are allocated on the heap.
Can cause garbage collection?
Creating a struct instance cannot cause a garbage collection (unless the constructor directly or indirectly creates a reference type instance) whereas creating a reference type instance can cause garbage collection.
Meaning of this?
In a class, this is classified as a value, and thus cannot appear on the left hand side of an assignment, or be used as a ref/out parameter. For example:
class Indirect { //... public void Method(Indirect that) { RefParameter(ref this); // compile-time error OutParameter(out this); // compile-time error this = that; // compile-time error } //... }
In a struct, this is classified as an out parameter in a constructor and as a ref parameter in all other function members. Thus it is possible to modify the entire structure by assigning to this or passing this as a ref/out parameter. For example:struct Direct { //... public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } //... }
Furthermore, you can reassign a whole struct even when the struct contains readonly fields!struct Direct { public Direct(int value) { Field = value; } public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } public readonly int Field; } class Show { static void Main() { Direct s = new Direct(42); Console.WriteLine(s.Field); // writes 42 s.Reassign(new Direct(24)); Console.WriteLine(s.Field); // writes 24 } }
Note however that when you call a method on a readonly value-type field, the method call is made on a copy of the field.struct Direct { // as above } class Caller { public void Method() { Console.WriteLine(d.Field); // writes 42 d.Reassign(new Direct(24)); Console.WriteLine(d.Field); // writes 42! } private readonly Direct d = new Direct(42); } class Show { static void Main() { Caller c = new Caller(); c.Method(); } }
Always have a default constructor?
A struct always has a built-in public default constructor.
class DefaultConstructor { static void Eg() { Direct yes = new Direct(); // always compiles ok InDirect maybe = new InDirect(); // compiles if c'tor exists and is accessible //... } }
This means that a struct is always instantiable whereas a class might not be since all its constructors could be private.class NonInstantiable { private NonInstantiable() // ok { } } struct Direct { private Direct() // compile-time error { } }
Default construction triggers static constructor?
A structs static constructor is not triggered by calling the structs default constructor. It is for a class.
struct Direct { static Direct() { Console.WriteLine("This is not written"); } } class NotTriggered { static void Main() { Direct local = new Direct(); } }
Can be null?
A struct instance cannot be null.
class Nullness { static void Eg(Direct s, Indirect c) { if (s == null) ... // compile-time error if (c == null) ... // compiles ok } }
Use with the as operator?
A struct type cannot be the right hand side operand of the as operator.
class Fragment { static void Eg(Direct s, Indirect c) { Direct no = s as Direct; // compile-time error InDirect yes = c as InDirect; // compiles ok //... } }
Can be locked?
A struct type expression cannot be the operand of a lock statement.
class LockStatement { static void Eg(Direct s, InDirect c) { lock(s) { ... } // compile-time error lock(c) { ... } // compiles ok } }
Can have a destructor?
A struct cannot have a destructor. A destructor is just an override of object.Finalize in disguise, and structs, being value types, are not subject to garbage collection.
struct Direct { ~Direct() {} // compile-time error } class InDirect { ~InDirect() {} // compiles ok }
And the CIL for ~Indirect() looks like this:
.method family hidebysig virtual instance void Finalize() cil managed { // ... } // end of method Indirect::Finalize
Default field layout?
The default [StructLayout] attribute (which lives in the System.Runtime.InteropServices namespace) for a struct is LayoutKind.Sequential whereas the default StructLayout for a class is LayoutKind.Auto. (And yes, despite its name you can tag a class with the StructLayout attribute.) In other words the CIL for this:
public struct Direct { //... }
looks like this:
.class public sequential ansi sealed beforefieldinit Direct extends [mscorlib]System.ValueType { //... }
whereas the CIL for this:
public sealed class InDirect { //... }
looks like this:
.class public auto ansi sealed beforefieldinit Indirect extends [mscorlib]System.Object { //... }
Can be a volatile field?
You can’t declare a user-defined struct type as a volatile field but you can declare a user-defined class type as a volatile field.
class Bad { private volatile Direct field; // compile-time error } class Good { private volatile Indirect field; // compiles ok }
Can have synchronized methods?
You can’t use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a struct type (if you call the method you get a runtime TypeLoadException) whereas you can use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a class type.
using System.Runtime.CompilerServices; class Indirect { [MethodImpl(MethodImplOptions.Synchronized)] // compiles and runs ok public void Method() { //... } } struct Direct { [MethodImpl(MethodImplOptions.Synchronized)] // compiles ok, runtime TypeLoadException public void Method() { //... } }
Can be pointed to?
Clause 25.2 of the C# standard defines an unmanaged type as any type that isn’t a reference type and doesn’t contain reference-type fields at any level of nesting. That is, one of the following:
- Any simple value type (11.1.3, eg byte, int, long, double, bool, etc).
- Any enum type.
- Any pointer type.
- Any user-defined struct-type that contains fields of unmanaged types only.
class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(Indirect * ptr = &variable) // compile-time error { //... } } } }
If you want to fix an unmanaged instance you have to do so by fixing it through an unmanaged field. For example:class Indirect { public int fixHandle; } class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(int * ptr = &variable.fixHandle) // compiles ok { //... } } } }
In contrast, you can (nearly) always take the address of an unmanaged instance.struct Direct { // no reference fields at any level of nesting } class SimpleCase { static void Main() { Direct variable = new Direct(); unsafe { Direct * ptr = &variable; // compiles ok //... } } }
However, you have to take the address inside a fixed statement if the variable is moveable (subject to relocation by the garbage collector, see 25.3 and example above). Also, you can never take the address of a volatile field.So, in summary, you can never create a pointer to a class type but you sometimes create a pointer to a struct type.
Can be stackalloc’d?
You can only use stackalloc on unmanaged types. Hence you can never use stackalloc on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { Indirect * array = stackalloc Indirect[42]; // compile-time error //... } } }
Where as you can use stackalloc on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { Direct * array = stackalloc Direct[42]; // compiles ok //... } } }
Can be sizeof’d?
You can only use sizeof on unmanaged types. Hence you can never use sizeof on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { int size = sizeof(Indirect); // compile-time error //... } } }
Where as you can use sizeof on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { int size = sizeof(Direct); // compiles ok //... } } }
How to initialize fields?
The fields of a class have a default initialization to zero/false/null. The fields of a struct have no default value.
struct Direct { public int Field; } class Indirect { public Indirect() { } //... public int Field; } class Defaults { static void Main() { Direct s; Console.WriteLine(s.Field); // compile-time error Indirect c = new Indirect(); Console.WriteLine(c.Field); // compiles ok } }
You can initialize fields in a class at their point of declaration. For example:
class Indirect { //... private int field = 42; }
You can’t do this for fields in a struct. For example:struct Direct { //... private int field = 42; // compile-time error }
Fields in a struct have to be initialized in a constructor. For example:struct Direct { public Direct(int value) { field = value; } //... private int field; // compiles ok }
Also, the definite assignment rules of a struct are tracked on an individual field basis. This means you can bypass initialization and “assign” the fields of a struct one a time. For example:struct Direct { public int X, Y; } class Example { static void Main() { Direct d; d.X = 42; Console.WriteLine(d.X); // compiles ok Console.WriteLine(d.Y); // compile-time error } }
Inheritance differences?
- a struct is implicitly sealed, a class isn’t.
- a struct can’t be abstract, a class can.
- a struct can’t call : base() in its constructor whereas a class with no explicit base class can.
- a struct can’t extend another class, a class can.
- a struct can’t declare protected members (eg fields, nested types) a class can.
- a struct can’t declare abstract function members, an abstract class can.
- a struct can’t declare virtual function members, a class can.
- a struct can’t declare sealed function members, a class can.
- a struct can’t declare override function members, a class can. The one exception to this rule is that a struct can override the virtual methods of System.Object, viz, Equals(), and GetHashCode(), and ToString().
Equals behavior?
classes inherit Object.Equals which implements identity equality whereas structs inherit ValueType.Equals which implements value equality.
using System.Diagnostics; struct Direct { public Direct(int value) { field = value; } private int field; } class Indirect { public Indirect(int value) { field = value; } private int field; } class EqualsBehavior { static void Main() { Direct s1 = new Direct(42); Direct s2 = new Direct(42); Indirect c1 = new Indirect(42); Indirect c2 = new Indirect(42); bool structEquality = s1.Equals(s2); bool classIdentity = !c1.Equals(c2); Debug.Assert(structEquality); Debug.Assert(classIdentity); } }
Overriding Equals for structs should be faster because it can avoid reflection and boxing.struct Direct { public Direct(int value) { field = value; } public override bool Equals(object other) { return other is Direct && Equals((Direct)other); } public static bool operator==(Direct lhs, Direct rhs) { return lhs.Equals(rhs); } //... private bool Equals(Direct other) { return field == other.field; } private int field; }
-
C# struct/class differences
- Events are locked?
- Exist on stack or heap?
- Can cause garbage collection?
- Meaning of this?
- Always has a default constructor?
- Default construction triggers static construction?
- Can be null?
- Use with the as operator?
- Can be locked?
- Can have a destructor?
- Default field layout?
- Can be a volatile field?
- Can have synchronized methods?
- Can be pointed to?
- Can be stackalloc’d?
- Can be sizeof’d?
- How to initialize fields?
- Inheritance differences?
- Equals behavior
Events are locked?
Events declared in a class have their += and -= access automatically locked via a lock(this) to make them thread safe (static events are locked on the typeof the class). Events declared in a struct do not have their += and -= access automatically locked. A lock(this) for a struct would not work since you can only lock on a reference type expression.
Exist on stack or heap?
Value type local instances are allocated on the stack. Reference type local instances are allocated on the heap.
Can cause garbage collection?
Creating a struct instance cannot cause a garbage collection (unless the constructor directly or indirectly creates a reference type instance) whereas creating a reference type instance can cause garbage collection.
Meaning of this?
In a class, this is classified as a value, and thus cannot appear on the left hand side of an assignment, or be used as a ref/out parameter. For example:
class Indirect { //... public void Method(Indirect that) { RefParameter(ref this); // compile-time error OutParameter(out this); // compile-time error this = that; // compile-time error } //... }
In a struct, this is classified as an out parameter in a constructor and as a ref parameter in all other function members. Thus it is possible to modify the entire structure by assigning to this or passing this as a ref/out parameter. For example:struct Direct { //... public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } //... }
Furthermore, you can reassign a whole struct even when the struct contains readonly fields!struct Direct { public Direct(int value) { Field = value; } public void Reassign(Direct that) { RefParameter(ref this); // compiles ok OutParameter(out this); // compiles ok this = that; // compiles ok } public readonly int Field; } class Show { static void Main() { Direct s = new Direct(42); Console.WriteLine(s.Field); // writes 42 s.Reassign(new Direct(24)); Console.WriteLine(s.Field); // writes 24 } }
Note however that when you call a method on a readonly value-type field, the method call is made on a copy of the field.struct Direct { // as above } class Caller { public void Method() { Console.WriteLine(d.Field); // writes 42 d.Reassign(new Direct(24)); Console.WriteLine(d.Field); // writes 42! } private readonly Direct d = new Direct(42); } class Show { static void Main() { Caller c = new Caller(); c.Method(); } }
Always have a default constructor?
A struct always has a built-in public default constructor.
class DefaultConstructor { static void Eg() { Direct yes = new Direct(); // always compiles ok InDirect maybe = new InDirect(); // compiles if c'tor exists and is accessible //... } }
This means that a struct is always instantiable whereas a class might not be since all its constructors could be private.class NonInstantiable { private NonInstantiable() // ok { } } struct Direct { private Direct() // compile-time error { } }
Default construction triggers static constructor?
A structs static constructor is not triggered by calling the structs default constructor. It is for a class.
struct Direct { static Direct() { Console.WriteLine("This is not written"); } } class NotTriggered { static void Main() { Direct local = new Direct(); } }
Can be null?
A struct instance cannot be null.
class Nullness { static void Eg(Direct s, Indirect c) { if (s == null) ... // compile-time error if (c == null) ... // compiles ok } }
Use with the as operator?
A struct type cannot be the right hand side operand of the as operator.
class Fragment { static void Eg(Direct s, Indirect c) { Direct no = s as Direct; // compile-time error InDirect yes = c as InDirect; // compiles ok //... } }
Can be locked?
A struct type expression cannot be the operand of a lock statement.
class LockStatement { static void Eg(Direct s, InDirect c) { lock(s) { ... } // compile-time error lock(c) { ... } // compiles ok } }
Can have a destructor?
A struct cannot have a destructor. A destructor is just an override of object.Finalize in disguise, and structs, being value types, are not subject to garbage collection.
struct Direct { ~Direct() {} // compile-time error } class InDirect { ~InDirect() {} // compiles ok }
And the CIL for ~Indirect() looks like this:
.method family hidebysig virtual instance void Finalize() cil managed { // ... } // end of method Indirect::Finalize
Default field layout?
The default [StructLayout] attribute (which lives in the System.Runtime.InteropServices namespace) for a struct is LayoutKind.Sequential whereas the default StructLayout for a class is LayoutKind.Auto. (And yes, despite its name you can tag a class with the StructLayout attribute.) In other words the CIL for this:
public struct Direct { //... }
looks like this:
.class public sequential ansi sealed beforefieldinit Direct extends [mscorlib]System.ValueType { //... }
whereas the CIL for this:
public sealed class InDirect { //... }
looks like this:
.class public auto ansi sealed beforefieldinit Indirect extends [mscorlib]System.Object { //... }
Can be a volatile field?
You can’t declare a user-defined struct type as a volatile field but you can declare a user-defined class type as a volatile field.
class Bad { private volatile Direct field; // compile-time error } class Good { private volatile Indirect field; // compiles ok }
Can have synchronized methods?
You can’t use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a struct type (if you call the method you get a runtime TypeLoadException) whereas you can use the [MethodImpl(MethodImplOptions.Synchronized)] attribute on methods of a class type.
using System.Runtime.CompilerServices; class Indirect { [MethodImpl(MethodImplOptions.Synchronized)] // compiles and runs ok public void Method() { //... } } struct Direct { [MethodImpl(MethodImplOptions.Synchronized)] // compiles ok, runtime TypeLoadException public void Method() { //... } }
Can be pointed to?
Clause 25.2 of the C# standard defines an unmanaged type as any type that isn’t a reference type and doesn’t contain reference-type fields at any level of nesting. That is, one of the following:
- Any simple value type (11.1.3, eg byte, int, long, double, bool, etc).
- Any enum type.
- Any pointer type.
- Any user-defined struct-type that contains fields of unmanaged types only.
class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(Indirect * ptr = &variable) // compile-time error { //... } } } }
If you want to fix an unmanaged instance you have to do so by fixing it through an unmanaged field. For example:class Indirect { public int fixHandle; } class Bad { static void Main() { Indirect variable = new Indirect(); unsafe { fixed(int * ptr = &variable.fixHandle) // compiles ok { //... } } } }
In contrast, you can (nearly) always take the address of an unmanaged instance.struct Direct { // no reference fields at any level of nesting } class SimpleCase { static void Main() { Direct variable = new Direct(); unsafe { Direct * ptr = &variable; // compiles ok //... } } }
However, you have to take the address inside a fixed statement if the variable is moveable (subject to relocation by the garbage collector, see 25.3 and example above). Also, you can never take the address of a volatile field.So, in summary, you can never create a pointer to a class type but you sometimes create a pointer to a struct type.
Can be stackalloc’d?
You can only use stackalloc on unmanaged types. Hence you can never use stackalloc on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { Indirect * array = stackalloc Indirect[42]; // compile-time error //... } } }
Where as you can use stackalloc on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { Direct * array = stackalloc Direct[42]; // compiles ok //... } } }
Can be sizeof’d?
You can only use sizeof on unmanaged types. Hence you can never use sizeof on class types. For example:
class Indirect { //... } class Bad { static void Main() { unsafe { int size = sizeof(Indirect); // compile-time error //... } } }
Where as you can use sizeof on struct types that are unmanaged. For example:struct Direct { // no reference fields at any level of nesting } class Good { static void Main() { unsafe { int size = sizeof(Direct); // compiles ok //... } } }
How to initialize fields?
The fields of a class have a default initialization to zero/false/null. The fields of a struct have no default value.
struct Direct { public int Field; } class Indirect { public Indirect() { } //... public int Field; } class Defaults { static void Main() { Direct s; Console.WriteLine(s.Field); // compile-time error Indirect c = new Indirect(); Console.WriteLine(c.Field); // compiles ok } }
You can initialize fields in a class at their point of declaration. For example:
class Indirect { //... private int field = 42; }
You can’t do this for fields in a struct. For example:struct Direct { //... private int field = 42; // compile-time error }
Fields in a struct have to be initialized in a constructor. For example:struct Direct { public Direct(int value) { field = value; } //... private int field; // compiles ok }
Also, the definite assignment rules of a struct are tracked on an individual field basis. This means you can bypass initialization and “assign” the fields of a struct one a time. For example:struct Direct { public int X, Y; } class Example { static void Main() { Direct d; d.X = 42; Console.WriteLine(d.X); // compiles ok Console.WriteLine(d.Y); // compile-time error } }
Inheritance differences?
- a struct is implicitly sealed, a class isn’t.
- a struct can’t be abstract, a class can.
- a struct can’t call : base() in its constructor whereas a class with no explicit base class can.
- a struct can’t extend another class, a class can.
- a struct can’t declare protected members (eg fields, nested types) a class can.
- a struct can’t declare abstract function members, an abstract class can.
- a struct can’t declare virtual function members, a class can.
- a struct can’t declare sealed function members, a class can.
- a struct can’t declare override function members, a class can. The one exception to this rule is that a struct can override the virtual methods of System.Object, viz, Equals(), and GetHashCode(), and ToString().
Equals behavior?
classes inherit Object.Equals which implements identity equality whereas structs inherit ValueType.Equals which implements value equality.
using System.Diagnostics; struct Direct { public Direct(int value) { field = value; } private int field; } class Indirect { public Indirect(int value) { field = value; } private int field; } class EqualsBehavior { static void Main() { Direct s1 = new Direct(42); Direct s2 = new Direct(42); Indirect c1 = new Indirect(42); Indirect c2 = new Indirect(42); bool structEquality = s1.Equals(s2); bool classIdentity = !c1.Equals(c2); Debug.Assert(structEquality); Debug.Assert(classIdentity); } }
Overriding Equals for structs should be faster because it can avoid reflection and boxing.struct Direct { public Direct(int value) { field = value; } public override bool Equals(object other) { return other is Direct && Equals((Direct)other); } public static bool operator==(Direct lhs, Direct rhs) { return lhs.Equals(rhs); } //... private bool Equals(Direct other) { return field == other.field; } private int field; }