-
Notifications
You must be signed in to change notification settings - Fork 7
Initial draft of MEX best practice #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rpurser47
wants to merge
103
commits into
main
Choose a base branch
from
mex-wip
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+330
−0
Open
Changes from all commits
Commits
Show all changes
103 commits
Select commit
Hold shift + click to select a range
00d5e56
Initial draft of MEX best practice
rpurser47 7fc50f8
Add badges and under construction sign
rpurser47 be7f298
Use a little more standard markdown
rpurser47 2569d24
Incorporate feedback
rpurser47 fe2ce4b
Added notes on toolbox/private
914c521
Added a bit more about private folder
82e5b8e
Added a link to private functions
8119a6d
Noted on key concepts added
ced478e
Added notes for gateway function
0fbb623
Added mexext notes for mexext
7e6e02a
Note on single source and multiple source mex functions
0792224
Added notes for the mexfunctions folder
c930145
Notes on multiple source MEX functions
f48fbad
updated the folder structure
747b6d8
Described the organization of files within cpp folder for the arithme…
bf983fa
Added content for organizing mex functions
cb92d20
Added notes for placing mexfunctions within private folder
23a0ea6
Added the build script
51b6e58
Minor update to the buildfile
27a0c32
Minor updates
5f3dc60
reorganized the mex source files section
cce3e12
Added the reasoning for Mex suffix
aa9ceb9
Added project root, to list of definitions
7b0f30b
Added notes for multiple source mex.
b9fabe2
Refined the language for source code organization
411a06a
Added notes for building mex functions
4be3315
Added notes on incorporating external libraries
2a806d1
Added notes for managing external library headers
91720b6
Added notes for compile time binaries
7936879
Added the table for file extensions and folder names
7a0eb7d
Added notes for out of process mex functions
b9cd463
added notes for runtime libraries
199286f
renamed runtime to execution time
15b708f
Added the example folder structure for execution time and compile tim…
0fc9ed8
minor update
ebd5a2b
Added notes for header only libraries
42a42bc
Added notes for test
f8e1bb0
Minor updates
8408924
Added notes on loader search path
499e969
Updated the notes for buildfile
6290847
Completed the build section
dc1d893
Adding notes for scenario 1
3126cdb
Minor updates to the wording
778c7df
refactored the advantages of buildfile
9995b9c
minor beautification
9fa6bc8
Added the organization of files after build
4cae121
Updated the language
eaaff1e
minor update
4afd1b9
Added the first part for multiple source mex fuctions
f8b6f25
Updated after working meeting with Rob
d042978
Added the definition of MEX functions within the overview section.
0694bb4
updated the single source mex file use case
b6ffd62
Minor updates to build mex
5b9f9ed
Minor updates
42df472
moved the automation using buildfile to the top
823fcc5
minor update
067bf96
Responded to most of the feedback
fdae922
Added a note for older release
a84f2ec
Updated based on Rob's feedback
bf5454c
Updated the single Cpp file section
58346cc
Included multiple source mex files
1879dba
Test that rob can push changes.
rpurser47 6157996
Yes, Rob can push changes
rpurser47 1e84046
minor updates
c8e9ad3
Merge branch 'mex-wip' of github.com:bpancras/toolboxdesign into mex-wip
688e5b7
Added the build file for multiple source MEX functions
6e3f2b5
updated the section on static libraries
2e03fb6
Discussion with Rob
02b643a
One pass with Rob till external libraries
4896681
updated the build script
d9d3131
Reviewed and revised up to and including buildtool. Added todo list
rpurser47 6f20d2d
updated todo
rpurser47 f336039
Revised Creating a MEX function from multiple C++ source files
rpurser47 8df53dd
minor updates
4f73ed0
Added the folder structure and buildfile for multiple single source M…
5206dc2
Added multiple single source MEX functions section
d85e2ca
Checked the buildfile on 24b
7c5433b
Updated the build file to work on 24a.
0b0a455
Updated mexfunction scenario
rpurser47 3ad187f
Review and update external libraries section
rpurser47 aa2cc26
added note on static vs dynamic
rpurser47 fe51fcc
Minor change
af84435
Merge branch 'mex-wip' of github.com:bpancras/toolboxdesign into mex-wip
0b4476c
added tl;dr section
4e30404
improved the language and made it more consistent.
rpurser47 443501a
Added an appendix
07860de
Added an introduction to GitHub Actions
3f43757
Added the first section for GitHub Actions
96e7e0c
Added multi platform build as well
4ecfcf6
Updated the buildfiles to work on 24b
70700b3
Minor updates
9ab9bd8
Tweaks to the TL;DR and syntax stuff
rpurser47 64cba4d
Merge branch 'mex-wip' of https://github.com/bpancras/toolboxdesign i…
rpurser47 4c743e0
update todo
rpurser47 9da5482
Updated the GitHub Actions section
8d82e66
Merge branch 'mex-wip' of github.com:bpancras/toolboxdesign into mex-wip
afdd7c0
Removed ToDo
b8b2878
Minor updates to the text
fdb342e
Added a mexTask that invokes the mex command directly
7ca1b24
Added a MEX task for multiple single source MEX functions
3fad78c
* Fixed Typos and minor formatting.
rpurser47 15155e6
Merge branch 'mex-wip' of https://github.com/mathworks/toolboxdesign …
rpurser47 362b4c8
Merge remote-tracking branch 'origin/main' into mex-wip
rpurser47 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
# MATLAB MEX Best Practice | ||
|
||
> [!IMPORTANT] | ||
> This page is under construction and review | ||
|
||
  | ||
|
||
Welcome to the MATLAB® MEX Best Practice guide, which extends [MATLAB Toolbox Best Practices](./README.md). This document focuses on integrating [MEX functions](https://www.mathworks.com/help/matlab/call-mex-file-functions.html) into your MATLAB toolbox. MEX functions enable you to harness the power of C, C++, and Fortran code within MATLAB. In this document when we say "C++", we mean "C, C++, and Fortran." | ||
|
||
## TL;DR | ||
- C++ code goes into the `cpp` folder. | ||
- Each MEX function is in its own folder with a `Mex` suffix | ||
- Place the built MEX functions in a `private` folder inside your `toolbox` folder. MEX functions should only be called from within your toolbox in order to increase reliability | ||
- External libraries are placed within a platform specific folder in the `private` folder and added to the system path | ||
- We recommend using the [`mexhost`](https://www.mathworks.com/help/matlab/ref/mexhost.html) command to increase reliability | ||
- Use a [`MexTask`](https://www.mathworks.com/help/matlab/ref/matlab.buildtool.tasks.mextask-class.html) in your [`buildfile.m`](https://www.mathworks.com/help/matlab/build-automation.html) for consistent builds | ||
|
||
## Overview | ||
|
||
[MEX functions](https://www.mathworks.com/help/matlab/call-mex-file-functions.html) are compiled functions that bridge the gap between MATLAB and C++. They behave like a MATLAB function and you must build them for each operating system you want to run on. You can determine the MEX file extension (for example, `.mexw64` in Microsoft Windows) for your operating system using [`mexext`](https://www.mathworks.com/help/matlab/ref/mexext.html). This guide will navigate you through the process of integrating of MEX functions, ensuring smooth implementation in both development and production environments. This makes it easier for others to understand and contribute to your toolbox. | ||
|
||
To illustrate these best practices, we've created a sample project: The Arithmetic Toolbox, available on [GitHub](https://github.com/mathworks/arithmetic). We'll reference this toolbox throughout the guide to demonstrate practical applications of these principles. For key concepts, refer to the [Toolbox Best Practices](./README.md). | ||
|
||
## MEX function from a single C++ source file | ||
The most common case is when you have a single C++ MEX source file. You do not distribute the source file to toolbox users, only the compiled MEX function. We recommend keeping the C++ MEX source files outside of the `toolbox` folder in a folder focused on C++ code, `cpp`. | ||
|
||
For each MEX function in the `cpp` folder, create a folder with the name that matches the name of your MEX function. This folder should end with `Mex` to indicate that all the files within the folder are associated with a single MEX function. | ||
|
||
Our example toolbox has a `cpp` folder in the root folder, with an `invertMex` subfolder inside. The C++ code `invertMex.cpp` is within this folder. While the `.cpp` file does not have to match the folder name, it can be convenient to do this in simple cases. | ||
|
||
``` text | ||
arithmetic/ | ||
├───cpp/ | ||
│ └───invertMex/ | ||
│ └───invertMex.cpp | ||
├───toolbox/ | ||
... | ||
└───arithmetic.prj | ||
``` | ||
### Building MEX functions | ||
|
||
Use the [`mex`](https://www.mathworks.com/help/matlab/ref/mex.html) command to compile `invertMex.cpp` into MEX functions. Place your compiled MEX functions in a `private` folder within the `toolbox` folder (see below). You need to provide the path of `invertMex.cpp` and path of the `private` folder as inputs to the `mex` command. | ||
|
||
``` matlab | ||
>> source = fullfile("cpp", "invertMex", "*.cpp"); | ||
>> destination = fullfile("toolbox", "private"); | ||
>> mex(source, "-outdir", destination, "-output", "invertMex") | ||
``` | ||
The `mex` command would create the MEX function and place it within the `private` folder. | ||
|
||
Our example toolbox has: | ||
|
||
1. A `invertMex.mexw64` file in the `toolbox/private` folder | ||
2. The MATLAB function `invertNumber.m` within the `toolbox` folder calls into the `invertMex` MEX function. We recommend using an [arguments block](https://www.mathworks.com/help/matlab/ref/arguments.html) in `invertNumber` to validate the inputs before passing it to `invertMex`. | ||
|
||
``` text | ||
arithmetic/ | ||
├───cpp/ | ||
│ └───invertMex/ | ||
│ └───invertMex.cpp | ||
├───toolbox/ | ||
| ├───private/ | ||
| | └───invertMex.mexw64 | ||
| ├... | ||
| └───invertNumber.m | ||
└───arithmetic.prj | ||
``` | ||
### Additional Notes | ||
* **Why put the MEX functions within a private folder?** By putting it in a [`private`](https://www.mathworks.com/help/matlab/matlab_prog/private-functions.html) folder, you restrict your users from calling the MEX function directly. Even minor errors in a MEX function will crash MATLAB, especially if they receive unexpected inputs. By limiting access to MEX functions to a MATLAB function that you control, you ensure that only what you expect will be passed as input to the MEX function, preventing errors from unexpected or unhandled inputs. | ||
* **Out of process MEX host:** We recommend [Out-of-Process Execution of C++ MEX Functions](https://www.mathworks.com/help/matlab/matlab_external/out-of-process-execution-of-c-mex-functions.html). This prevents coding errors in your C++ MEX function from crashing MATLAB and allows you to use some third-party libraries that are not compatible with MATLAB. Use the [`mexhost`](https://www.mathworks.com/help/matlab/ref/mexhost.html) command. Note that `mexhost` is only supported for C++ MEX functions. | ||
* **Using git:** In git source control systems, we recommend that you *do not* keep compiled MEX functions under version control, as they are derived files. Add `*.mex*` to your `.gitignore` file. This is part of the [standard .gitignore file](https://github.com/mathworks/gitignore/blob/main/Global/MATLAB.gitignore) for MATLAB. | ||
|
||
### Automation using `buildtool` | ||
|
||
Running the `mex` command for each MEX function can be tedious and error prone. MATLAB’s [`buildtool`](https://www.mathworks.com/help/matlab/ref/buildtool.html), introduced in R2022b, can automate this and many other repetitive processes for you. The following `buildfile.m` creates a [`MexTask`](https://www.mathworks.com/help/matlab/ref/matlab.buildtool.tasks.mextask-class.html), that builds your MEX functions. | ||
|
||
*This works in R2024b and later -- see the end of this document for a version supported in R2022a-R2024a.* | ||
``` matlab | ||
function plan = buildfile | ||
|
||
plan = buildplan(); | ||
|
||
mexOutputFolder = fullfile("toolbox","private"); | ||
|
||
% Compile Cpp source code within cpp/*Mex into MEX functions | ||
foldersToMex = plan.files(fullfile("cpp", "*Mex")).select(@isfolder); | ||
for folder = foldersToMex.paths | ||
[~, folderName] = fileparts(folder); | ||
plan("mex:"+folderName) = matlab.buildtool.tasks.MexTask(fullfile(folder, "**/*.cpp"), ... | ||
mexOutputFolder, ... | ||
Filename=folderName); | ||
end | ||
plan("mex").Description = "Build MEX functions"; | ||
end | ||
``` | ||
|
||
Our example toolbox adds a `buildfile.m` to automate the building of the MEX functions. When you run "`buildtool mex`", the `invertMex.mexw64` gets created within the `private` folder. | ||
|
||
``` text | ||
arithmetic/ | ||
├───cpp/ | ||
│ └───invertMex/ | ||
│ └───invertMex.cpp | ||
├───toolbox/ | ||
| ├───private/ | ||
| | └───invertMex.mexw64 | ||
| ├... | ||
| ├───buildfile.m | ||
| └───invertNumber.m | ||
└───arithmetic.prj | ||
``` | ||
|
||
## MEX function from multiple C++ source files | ||
|
||
The above pattern extends to MEX functions that has multiple C++ source files. One of the source files must contain the [gateway](https://www.mathworks.com/help/matlab/matlab_external/gateway-routine.html) function. Place all the source files under a single folder. | ||
|
||
Our example toolbox adds two `.cpp` files in the `subtractMex` folder, which builds to the `subtractMex.mexw64` binary in the `private` folder. `subtractNumber.m` provides a user accessible version that error checks before calling `SubtractMex`: | ||
|
||
``` text | ||
arithmetic/ | ||
├───cpp/ | ||
│ ├───subtractMex/ | ||
│ │ ├───subtract.cpp % Implements gateway function | ||
│ │ └───subtractImpl.cpp % other features | ||
│ └───invertMex/ | ||
│ └───invertMex.cpp | ||
├───toolbox/ | ||
| ├───private/ | ||
| | ├───invertMex.mexw64 | ||
| | └───subtractMex.mexw64 | ||
| ├─... | ||
| ├───subtractNumber.m | ||
| └───invertNumber.m | ||
├───arithmetic.prj | ||
└───buildfile.m | ||
``` | ||
|
||
The `buildfile.m` is the same as before. | ||
|
||
## Handling a large number of MEX functions | ||
If you have many MEX functions, each in its own C++ source file, the approach of placing each C++ source file in a separate folder is cumbersome. In such scenarios we recommend an alternate pattern: move the source files within a `mexfunctions` subfolder under the `cpp` folder. | ||
|
||
``` text | ||
arithmetic/ | ||
├───cpp/ | ||
│ └───mexfunctions/ | ||
| ├───powerMex.cpp | ||
│ └───squareRootMex.cpp | ||
├───toolbox/ | ||
| ├───private/ | ||
| | ├───powerMex.mexw64 | ||
| | └───squareRootMex.mexw64 | ||
| ├─... | ||
| ├───powerNumber.m | ||
| └───divideNumber.m | ||
├───arithmetic.prj | ||
└───buildfile.m | ||
``` | ||
The build file for building these MEX functions is slightly different. | ||
|
||
``` matlab | ||
function plan = buildfile | ||
% !!Revise to work in 24b!! | ||
plan = buildplan(); | ||
mexOutputFolder = fullfile("toolbox","private"); | ||
|
||
% Compile all the folders inside cpp/*Mex.cpp into MEX functions | ||
filesToMex = plan.files(fullfile("cpp", "mexfunctions", "*.cpp")); | ||
for cppFile = filesToMex.paths | ||
[~, fileName] = fileparts(cppFile); | ||
plan("mex:"+fileName) = matlab.buildtool.tasks.MexTask(cppFile, ... | ||
mexOutputFolder); | ||
end | ||
plan("mex").Description = "Build MEX functions"; | ||
end | ||
``` | ||
|
||
## Incorporating External Libraries | ||
|
||
You can call libraries implemented in C++ using MEX functions. Since MEX source files are just C++ source code, they use standard C++ syntax to access external libraries. | ||
|
||
### External Library Header Files (`.h`,`.hpp`) | ||
Create an `include` folder within the `cpp` folder and put the external library [header files](https://www.learncpp.com/cpp-tutorial/header-files/) within this folder. Use the [`-I`](https://www.mathworks.com/help/matlab/ref/mex.html#btw17rw-1-option1optionN) argument to the [mex function](https://www.mathworks.com/help/matlab/ref/mex.html) to specify that header files are in the `include` folder. | ||
|
||
### Incorporating a Static Library (`.lib`) | ||
Some MEX functions incorporate [static libraries](https://www.learncpp.com/cpp-tutorial/a1-static-and-dynamic-libraries/) that are compiled into your MEX function. Place these binaries under platform specific folders within the `library` folder. Use the names from the [`computer('arch')`](https://www.mathworks.com/help/matlab/ref/computer.html) command in MATLAB for the folder names. The table below provides a summary of the folder names and file extensions used for static libraries for popular operating systems. | ||
|
||
| Platform | Folder name | Binary Extension | | ||
| :---------------- | :------ | :------ | | ||
| Linux | glnxa64 | .a | | ||
| Windows | win64 | .lib | | ||
| ARM / Intel Mac | maca64 | .dylib | | ||
|
||
``` text | ||
zlibStatic/ | ||
: | ||
├───cpp/ | ||
| ├───include/ | ||
| │ ├───zlibconf.h | ||
| │ └───zlib.h | ||
| ├───library/ | ||
| │ ├───glnxa64 | ||
| | | └───libz.a | ||
| │ ├───win64 | ||
| | | └───libz.lib | ||
| │ └───maca64 | ||
| | └───libz.dylib | ||
│ └───mexfunctions/ | ||
│ └───deflateMex.cpp | ||
├───toolbox/ | ||
| ├───deflate.m | ||
| └───private/ | ||
| ├───deflateMex.mexw64 | ||
| ├───deflateMex.mexa64 | ||
| └───deflateMex.mexmaca64 | ||
├───zlibStatic.prj | ||
└───buildfile.m | ||
``` | ||
|
||
### Calling a Dynamic Library | ||
[Dynamic libraries](https://www.learncpp.com/cpp-tutorial/a1-static-and-dynamic-libraries/) are required for running the MEX functions and must ship to the users. Place the binaries within the `private` folder under the `toolbox` folder to ensure the library gets shipped to the user. Use the [`-L`](https://www.mathworks.com/help/matlab/ref/mex.html#btw17rw-1-option1optionN) argument to the [`mex`](https://www.mathworks.com/help/matlab/ref/mex.html) command to specify the location and the name of the runtime library. | ||
|
||
**Note:** If you have a choice between using a static or dynamic library with your MEX function, we recommend using a static library. Static libraries are incorporated inside your MEX function, making your MEX function more robust and reliable. | ||
|
||
|
||
``` text | ||
zlibShared/ | ||
: | ||
├───cpp/ | ||
| ├───include/ | ||
| │ └───zlib.h | ||
│ └───mexfunctions/ | ||
│ └───deflateMex.cpp | ||
├───toolbox/ | ||
| ├───deflate.m | ||
| └───private/ | ||
| ├───libz.so | ||
| ├───libz.lib | ||
| ├───libz.dylib | ||
| ├───deflateMex.mexw64 | ||
| ├───deflateMex.mexa64 | ||
| └───deflateMex.mexmaca64 | ||
├───zlibShared.prj | ||
└───buildfile.m | ||
``` | ||
* For projects with complex dependencies, consider adopting dependency management tools like [Conan](https://conan.io/) which can significantly simplify library management across different platforms. | ||
* Depending on the platform, dynamic libraries require adding their path to the operating system search path. These search paths are often set using environment variables. In case of C++ libraries you can use the `EnvironmentVariables` option of [`mexhost`](https://www.mathworks.com/help/matlab/ref/mexhost.html) to set-up the loader's path. The table below shows the environment variable for different operating systems. | ||
|
||
| Operating system | Environment variable for loader's search path | | ||
| :-------- | :-------------------------------------------- | | ||
| Linux | `LD_LIBRARY_PATH` | | ||
| Windows | `PATH` | | ||
| Mac | `DYLD_LIBRARY_PATH` | | ||
|
||
## Automating Builds with GitHub Actions | ||
[MATLAB Actions](https://github.com/matlab-actions) build, test and deploy your toolbox as a part of [GitHub Action](https://docs.github.com/en/actions). MathWorks offers free licenses for public GitHub repositories, including support for Windows, Mac and Linux. If your GitHub repository is private, visit [this webpage](https://github.com/matlab-actions/setup-matlab?tab=readme-ov-file#use-matlab-batch-licensing-token). | ||
|
||
Within a GitHub Action, you can invoke MATLAB's `buildtool` using [matlab-actions/run-command](https://github.com/matlab-actions/run-command/). The build tasks that you already configured for local development like building MEX functions, running tests and packaging the toolbox can all be reused within your GitHub Workflow. Our example uses a [matrix strategy](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow) in the GitHub Action to build MEX functions on different operating systems. | ||
|
||
```yaml | ||
# Excerpt from the YAML file | ||
Jobs: | ||
mexBuild: | ||
# Build MEX function on different operating systems using matrix strategy for operating systems. | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest, windows-latest, mac-latest] | ||
runs-on: ${{ matrix.os }} | ||
... | ||
|
||
steps: | ||
- name: Check out repository | ||
uses: actions/checkout@v4 | ||
- name: Set up MATLAB | ||
uses: matlab-actions/setup-matlab@v2 | ||
- name: Run build | ||
uses: matlab-actions/run-build@v2 | ||
with: | ||
task: mex | ||
... | ||
``` | ||
For the full YAML file refer to [`mexbuild.yml`](https://github.com/mathworks/arithmetic/blob/main/.github/workflows/mexbuild.yml). | ||
|
||
|
||
|
||
## Conclusion | ||
|
||
By following to these best practices, you'll create a reliable, maintainable, and user-friendly MATLAB toolbox that harnesses the full potential of MEX functions. Through effective project structure organization, build automation, and dependency management, you can focus on delivering great solutions to your users. | ||
|
||
We welcome your input! For further details, suggestions, or to contribute, please [open an issue](https://github.com/mathworks/toolboxdesign/issues/new/choose). | ||
|
||
## Appendix: Buildfiles for MATLAB R2022a-R2024a | ||
|
||
``` matlab | ||
function plan = buildfile | ||
% Create a plan from the task functions | ||
plan = buildplan(localfunctions); | ||
plan("mex").Inputs = files(plan, fullfile("cpp","*Mex")); | ||
plan("mex").Outputs = files(plan, fullfile("toolbox","private")); | ||
|
||
end | ||
|
||
function mexTask(context) | ||
for f = context.Task.Inputs.paths | ||
mex(fullfile(f, "*.cpp"), "-outdir", context.Task.Outputs.paths) | ||
end | ||
end | ||
``` | ||
|
||
|
||
``` matlab | ||
function plan = buildfile | ||
% Create a plan from the task functions | ||
plan = buildplan(localfunctions); | ||
plan("mex").Inputs = files(plan, fullfile("cpp","mexfunctions","*.cpp")); | ||
plan("mex").Outputs = files(plan, fullfile("toolbox","private")); | ||
|
||
end | ||
|
||
function mexTask(context) | ||
for mexSource = context.Task.Inputs.paths | ||
mex(mexSource, "-outdir", context.Task.Outputs.paths) | ||
end | ||
end | ||
``` | ||
|
||
--- | ||
[](https://creativecommons.org/licenses/by/4.0/) | ||
|
||
Copyright © 2025, The MathWorks, Inc. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.