the code for COUNT_ARGS_IMPL is
#define COUNT_ARGS_IMPL(macro, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) _1
Why is the visual studio compiler having it grab the entirety of the previous macro's var_args in the first argument? If I try the same code but not nested it works fine. Its normally supposed to return macro##N
but I did this while testing and it doesn't make sense.
I tested without passing the macro name to count args and having the count precalculated before the shader_program macro runs but the issue persists.
if you want the whole file its this.
#pragma once
#define GLAD_GL_IMPLEMENTATION
#include <glad/glad.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <linmath.h>
static std::string readShader(const char const* location)
{
char fileName[256] = "shaders\\";
strcat_s(fileName, location);
std::ifstream stream(fileName);
if (!stream)
{
std::cerr << "Failed to open file: " << fileName << std::endl;
return "";
}
std::stringstream buffer;
buffer << stream.rdbuf();
stream.close();
return buffer.str();
}
static GLuint create_shader(const char const* location, int shaderType)
{
std::string shaderText = readShader(location);
const char* cShaderText = shaderText.c_str();
const GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &cShaderText, NULL);
glCompileShader(shader);
GLint infoLength = 0;
char* infoLogBuffer;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
if (infoLength > 1)
{
infoLogBuffer = (char*)malloc(infoLength);
glGetShaderInfoLog(shader, infoLength, NULL, infoLogBuffer);
std::cout << infoLogBuffer << std::endl;
free(infoLogBuffer);
}
return shader;
}
int EndsWith(const char* str, const char* suffix)
{
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
#define str(x) #x
#define shader_prop(prop, type, element) \
GLint prop##_location; \
///##* program.##prop##_location - glGet##element##Location(program.shader, #prop);*##/
#define shader_uniform(prop, type) (prop, type, Uniform)
#define shader_attribute(prop, type) (prop, type, Attrib)
struct ShaderProgramShader {
const char* path;
int type;
};
#define generic_shader(path, type, extension) ShaderProgramShader {str(path.extension), type}
#define vert_shader(path) generic_shader(path, GL_VERTEX_SHADER, vert)
#define frag_shader(path) generic_shader(path, GL_FRAGMENT_SHADER, frag)
#define UNPAREN(...) __VA_ARGS__
#define COUNT_ARGS_IMPL(macro, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) _1
#define COUNT_ARGS(macro,...) COUNT_ARGS_IMPL(macro, __VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_LIST3(macro, ...) COUNT_ARGS(macro, __VA_ARGS__)
#define COUNT_LIST2(macro, ...) COUNT_LIST3(macro, __VA_ARGS__)
#define COUNT_LIST(macro, list) COUNT_LIST2(macro, UNPAREN list)
#define prop_attrib_define_p(name, type, element) type name;
#define prop_attrib_define_0(name, type, element)
#define prop_attrib_define_1(v1) prop_attrib_define_p v1
#define prop_attrib_define_2(v1, v2) prop_attrib_define_p v1 prop_attrib_define_1 (v2)
#define prop_attrib_define_3(v1, ...) prop_attrib_define_p v1 prop_attrib_define_2 (__VA_ARGS__)
#define prop_attrib_define_4(v1, ...) prop_attrib_define_p v1 prop_attrib_define_3 (__VA_ARGS__)
#define prop_attrib_define_5(v1, ...) prop_attrib_define_p v1 prop_attrib_define_4 (__VA_ARGS__)
#define prop_attrib_define_6(v1, ...) prop_attrib_define_p v1 prop_attrib_define_5 (__VA_ARGS__)
#define prop_attrib_define_7(v1, ...) prop_attrib_define_p v1 prop_attrib_define_6 (__VA_ARGS__)
#define prop_attrib_define_8(v1, ...) prop_attrib_define_p v1 prop_attrib_define_7 (__VA_ARGS__)
#define prop_attrib_define_9(v1, ...) prop_attrib_define_p v1 prop_attrib_define_8 (__VA_ARGS__)
#define prop_attrib_define_10(v1, ...) prop_attrib_define_p v1 prop_attrib_define_9 (__VA_ARGS__)
#define prop_location_define_p(name, type, element) GLint name##_location;
#define prop_location_define_0(name, type, element)
#define prop_location_define_1(v1) prop_location_define_p v1
#define prop_location_define_2(v1, v2) prop_location_define_p v1 prop_location_define_1 (v2)
#define prop_location_define_3(v1, ...) prop_location_define_p v1 prop_location_define_2 (__VA_ARGS__)
#define prop_location_define_4(v1, ...) prop_location_define_p v1 prop_location_define_3 (__VA_ARGS__)
#define prop_location_define_5(v1, ...) prop_location_define_p v1 prop_location_define_4 (__VA_ARGS__)
#define prop_location_define_6(v1, ...) prop_location_define_p v1 prop_location_define_5 (__VA_ARGS__)
#define prop_location_define_7(v1, ...) prop_location_define_p v1 prop_location_define_6 (__VA_ARGS__)
#define prop_location_define_8(v1, ...) prop_location_define_p v1 prop_location_define_7 (__VA_ARGS__)
#define prop_location_define_9(v1, ...) prop_location_define_p v1 prop_location_define_8 (__VA_ARGS__)
#define prop_location_define_10(v1, ...) prop_location_define_p v1 prop_location_define_9 (__VA_ARGS__)
#define prop_location_assign_p(name, type, element) program.name##_location = glGet##element##Location(program.shader, #name);
#define prop_location_assign_0(name, type, element)
#define prop_location_assign_1(v1) prop_location_assign_p v1
#define prop_location_assign_2(v1, v2) prop_location_assign_p v1 prop_location_assign_1 (v2)
#define prop_location_assign_3(v1, ...) prop_location_assign_p v1 prop_location_assign_2 (__VA_ARGS__)
#define prop_location_assign_4(v1, ...) prop_location_assign_p v1 prop_location_assign_3 (__VA_ARGS__)
#define prop_location_assign_5(v1, ...) prop_location_assign_p v1 prop_location_assign_4 (__VA_ARGS__)
#define prop_location_assign_6(v1, ...) prop_location_assign_p v1 prop_location_assign_5 (__VA_ARGS__)
#define prop_location_assign_7(v1, ...) prop_location_assign_p v1 prop_location_assign_6 (__VA_ARGS__)
#define prop_location_assign_8(v1, ...) prop_location_assign_p v1 prop_location_assign_7 (__VA_ARGS__)
#define prop_location_assign_9(v1, ...) prop_location_assign_p v1 prop_location_assign_8 (__VA_ARGS__)
#define prop_location_assign_10(v1, ...) prop_location_assign_p v1 prop_location_assign_9 (__VA_ARGS__)
#define prop_uniform_assign_float(shader_name, name) shader_name##_set_##name(shader_name##ShaderProgram& program, float value) { glUniform1f(program.name##_location, value); }
#define prop_uniform_assign_vec3(shader_name, name) shader_name##_set_##name(shader_name##ShaderProgram& program, vec3 value) { glUniform3fv(program.name##_location, 1, value); }
#define prop_uniform_assign_mat4x4(shader_name, name) shader_name##_set_##name(shader_name##ShaderProgram& program, mat4x4 value) { glUniform4fv(program.name##_location, 1, GL_FALSE, (const GLfloat*)value); }
#define prop_uniform_assign_p(shader_name, name, type, element) prop_uniform_assign_##type(shader_name, name)
#define prop_uniform_assign_0(shader_name, name, type, element)
#define prop_uniform_assign_1(shader_name, v1) prop_uniform_assign_p (shader_name, UNPAREN v1 )
#define prop_uniform_assign_2(shader_name, v1, v2) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_1 (shader_name, v2)
#define prop_uniform_assign_3(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_2 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_4(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_3 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_5(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_4 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_6(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_5 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_7(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_6 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_8(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_7 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_9(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_8 (shader_name, __VA_ARGS__)
#define prop_uniform_assign_10(shader_name, v1, ...) prop_uniform_assign_p (shader_name, UNPAREN v1) prop_uniform_assign_9 (shader_name, __VA_ARGS__)
#define shader_program(shader_name, shaders, attributes, uniforms) \
typedef struct shader_name##ShaderProgram {\
GLuint shader;\
COUNT_LIST(prop_location_define_, attributes)\
COUNT_LIST(prop_location_define_, uniforms)\
} shader_name##ShaderProgram;\
typedef struct shader_name##Vertex {\
COUNT_ARGS(prop_attrib_define_, UNPAREN attributes) attributes\
} shader_name##Vertex;\
static void create_##shader_name##ShaderProgram(shader_name##ShaderProgram& program)\
{\
ShaderProgramShader shader_list[] UNPAREN shaders;\
GLuint created_shaders[sizeof(shader_list) / sizeof(ShaderProgramShader)];\
for(int i = 0; i < sizeof(shader_list) / sizeof(ShaderProgramShader); i++) {\
created_shaders[i] = create_shader(shader_list[i].path, shader_list[i].type);\
}\
program.shader = glCreateProgram();\
\
glLinkProgram(program.shader);\
\
for(int i = 0; i < sizeof(shader_list) / sizeof(ShaderProgramShader); i++) {\
glDeleteShader(created_shaders[i]);\
}\
\
COUNT_ARGS(prop_location_assign_, UNPAREN attributes) attributes\
COUNT_ARGS(prop_location_assign_, UNPAREN uniforms) uniforms\
}\
COUNT_ARGS(prop_uniform_assign_, UNPAREN uniforms) (shader_name, UNPAREN uniforms)\
//#define shader_program(shader_name, uniforms) \
COUNT_ARGS(prop_uniform_assign_, UNPAREN uniforms) (shader_name, UNPAREN uniforms)\
shader_program(Ocean, ({vert_shader(ocean)}), (0), (shader_uniform(mv, mat4x4), shader_uniform(perspective, mat4x4), 0, 2, 3))
With some more experimentation I think I figured out the problem. Arguments that are modified by macros before being passed to another macro will pass them as just one argument, even if the modification results in comma separated arguments with no parenthesis. For example:
#define unparen(...) __VA_ARGS__
#define bar(arg1, arg2, arg3) (arg1) (arg2) (arg3)
#define foo(f) bar(0, unparen f)
foo((1, 2, 3))
One would expect foo to expand like so:
bar(0, unparen (1, 2, 3))
-> bar(0, 1, 2, 3)
-> (0) (1) (2)
and it actually does up until the last step, as it takes the unparenthesized value as a whole argument, meaning the actual expansion becomes:
bar(0, unparen (1, 2, 3))
-> bar(0, |1, 2, 3|)
-> (0) (1, 2, 3) ()
The |
is added to visualize how the parameter is being passed and does not exist.
This can be fixed by adding the macro
#define call(macro, ...) macro (__VA_ARGS__)
Now foo can be defined as
#define foo (f) call(bar, 0, unparen f)
Because the creation of the bar macro is delayed, it can now accept each argument individually. The expansion becomes like so:
call(bar, 0, unparen(1, 2, 3))
-> call(bar, 0, |1, 2, 3|)
-> bar(0, 1, 2, 3)
-> (0) (1) (2)
Once again, |
is used to show where an argument that does not appear to be a single argument is passed as a single argument.
The final code is:
#define unparen(...) __VA_ARGS__
#define call(macro, ...) macro (__VA_ARGS__)
#define bar(arg1, arg2, arg3) (arg1) (arg2) (arg3)
#define foo(f) call(bar, 0, unparen f)
foo((1, 2, 3))