Compile Dialog in Visual Studio

I’m trying to get dialogc to compile with Visual Studio 2022. Why, you may ask? Well it’s an environment that I’m familiar with and if I get it to work I can start stepping through the code, set breakpoints and inspect variables during the compilation of a Dialog game. Hopefully this will mean greater understanding and a possibility to add meaningful comments.

This is more of a StackOverflow question, but anyway…

VS2022 doesn’t support VLAs (Variable Length Array) so statements like

struct wordmap_tally tallies[prg->ndictword + 1];

will generate an error. My understanding is that mallo should be used in these cases to allocate space on the stack, like

struct wordmap_tally* tallies = (struct wordmap_tally*)malloc((prg->ndictword + 1) * sizeof(struct wordmap_tally);

But how to handle statements like this?

struct astnode *bindings[def->nvar];

or multidimension arrays, like this?

uint16_t cost[n_old + 1][n_new + 1]
1 Like

Visual Studio seriously doesn’t support VLAs?? Those have been in the standard for a quarter of a century now!

For the second one, it should work just the same as the first:

struct astnode **bindings = malloc((def->nvar) * sizeof(struct astnode*));

Or you can use sizeof(void*) instead of sizeof(anything else*) because all pointers have to be the same size; up to you which is more readable (because arguably sizeof(struct astnode*) is easy to confuse with sizeof(struct astnode), while sizeof(void*) is not).

For the third one, it’s a little messier:

uint16_t (*cost)[n_new + 1] = malloc(sizeof(uint16_t[n_old + 1][n_new + 1]));

See here for more details.

1 Like

Thank you! I’m always a bit uncertain when handling pointers.

As I understand it, VLAs are a part of C. VS is primeraly a C++ compiler, but also compiles C. Recommended is to use vectors in C++.

GCC (and other compilers I’m sure) have VLAs as extra features, but it is not in the standard for C++.

From Wikipedia:

C99 introduced support for VLAs, although they were subsequently relegated in C11 to a conditional feature, which implementations are not required to support;[2][3] on some platforms, VLAs could be implemented formerly with alloca() or similar functions.

1 Like

Right, but Dialog is purely C, right?

I think so.

Maybe change malloc for alloca, assuming vs has it. Although watch for stack overflow.

Alternately, since this is a C++ compiler, you could just use new, right?

I went down another road.

  • I installed MinGW64 from MSYS2.
  • Created a CMake project inside VS2022.
  • Edited a CMakelists.txt for dialogc (from Makefile).
  • Compiled for Mingw64-Debug

(It was not as easy as it seems like from above. I will document the process in more details later on.)

And now I have it working as I want.

Example:

1 Like

As promised, a more detailed How-To…

Setting up Dialog to compile in Visual Studio 2022 with minGW64

  1. Install MSYS2 in C:\msys64 (https://www.msys2.org/). Use default settings for installation.

  2. Open a MSYS2 (any) terminal and install gcc toolchain for both ucrt64 and mingw64.

    pacman -S mingw-w64-ucrt-x86_64-gcc
    pacman -S mingw-w64-x86_64-gcc
    pacman -S make
  1. Use the MSYS2 MINGW64 terminal if you want to compile and link with MSVCRT for C (compatible with pre Win10 but lacks some features). Use MSYS2 UCRT64 terminal if you want to compile and link with UCRT for C (Win10+, more “modern”, UTF8). See Environments - MSYS2 for more details.

  2. To use gcc and Linux style commands in Windows CMD or PowerShell windows, add C:\msys64\ucrt64\bin and C:\msys64\usr\bin to the path environment variable (Settings/System/Advanced/Environment Variables).

  3. Create new CMake project in Visual Studio 2022 (check box for place solution and project in same directory).

  4. Delete the dummy files *.cpp and *.h that was created in the new project.

  5. Copy the Dialog \src directory to the newly created project.

  6. Add configurations for Mingw64 and Ucrt64 to CMakePresets.json.

    {
      "name": "Mingw64-debug",
      "displayName": "Mingw64 Debug",
      "inherits": "windows-base",
      "environment": {
        "PATH": "C:/msys64/mingw64/bin;$penv{PATH}"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "gcc.exe",
        "CMAKE_CXX_COMPILER": "g++.exe",
        "CMAKE_BUILD_TYPE": "Debug"
      }
    },
    {
      "name": "Mingw64-release",
      "displayName": "Mingw64 Release",
      "inherits": "Mingw64-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "Ucrt64-debug",
      "displayName": "Ucrt64 Debug",
      "inherits": "windows-base",
      "environment": {
        "PATH": "C:/msys64/ucrt64/bin;$penv{PATH}"
      },
      "cacheVariables": {
        "CMAKE_C_COMPILER": "gcc.exe",
        "CMAKE_CXX_COMPILER": "g++.exe",
        "CMAKE_BUILD_TYPE": "Debug"
      }
    },
    {
      "name": "Ucrt64-release",
      "displayName": "Ucrt64 Release",
      "inherits": "Ucrt64-debug",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    }
  1. Change CMakeList.txt (add_definitions and add_executable are new and/or changed from the default).
	# CMakeList.txt : CMake project for dialogc, include source and define
	# project specific logic here.
	#
	cmake_minimum_required (VERSION 3.8)
	
	# Enable Hot Reload for MSVC compilers if supported.
	if (POLICY CMP0141)
	  cmake_policy(SET CMP0141 NEW)
	  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
	endif()
	
	add_definitions("-DVERSION=\"0m/03\"")
	
	project ("dialogc")
	
	# Add source to this project's executable.
	add_executable (dialogc "src/aavm.c"
							"src/accesspred.c"
							"src/arena.c"
							"src/ast.c"
							"src/backend.c"
							"src/backend_aa.c"
							"src/backend_z.c"
							"src/blorb.c"
							"src/compile.c"
							"src/crc32.c"
							"src/dumb_output.c"
							"src/dumb_report.c"
							"src/eval.c"
							"src/frontend.c"
							"src/parse.c"
							"src/runtime_z.c"
							"src/unicode.c"
	)
	
	if (CMAKE_VERSION VERSION_GREATER 3.12)
	  set_property(TARGET dialogc PROPERTY CXX_STANDARD 20)
	endif()
	
	# TODO: Add tests and install targets if needed.
  1. Compile and debug for Mingw64 or Ucrt64 should now work. Arguments for debugging can be added under menu Debug/Debug and Launch Settings for <project>.
  {
    "version": "0.2.1",
    "defaults": {},
    "configurations": [
      {
        "type": "default",
        "project": "CMakeLists.txt",
        "projectTarget": "dialogc.exe",
        "name": "dialogc.exe",
        "args": [
          "--version"
        ]
      }
    ]
  }
1 Like

I havn’t bothered setting up dgdebug for VS2022, because it’s 32-bit. But to compile it in MSYS2 you need:

  1. Install mingw32 gcc
    pacman -S mingw-w64-i686-gcc
  1. Start the MSYS2 MINGW32 environment with mingw32.exe.

  2. make dgdebug.exe in the Dialog /src.

To run dgdebug you need Glk.dll and ScaleGfx.dll in same directory.

This is awesome. Earlier on you wanted to use the VS debugging features. But with this, are you now using gdb instead, and does that now also work with VS?

Well yes. I can now set breakpoints, inspect variables, see memory and step through the code statement by statement while executing it.

1 Like

The first statement is just an array of pointers (as opposed to an array of objects). You can allocate it with malloc() or better - calloc(). So you can write:

struct astnode **bindings = (struct astnode **)calloc(def->nvar, sizeof(**bindings));

And you will get an array of pointers.

To allocate two-dimensional array, just multiply the sizes:

uint16_t *cost = (uint16_t *)calloc((n_old + 1) * (n_new + 1), sizeof(*cost));

Now you can access elements by using formula:

cost[y * width + x]; Where x and y are two-dimension indexes and width is a variable width of the array.

Alternatively alloc an array of arrays so you can use two brackets:

uint16_t **cost = (uint16_t **)calloc(n_old + 1, sizeof(**cost));
int i;
for (i = 0; i < n_old + 1; i++) cost[i] = (uint16_t *)calloc(n_new + 1, sizeof(*cost));

Now access elements by cost[y][x]

Free the memory with free() once you’re done with the arrays.

If you need to extend the array (or shrink it) use realloc() function.