I am new to OpenGL learning it on amazing website learnopengl.com I wanted to a convenient way to use shader programs thats why I created this struct, please review it.
ShaderProgram.h
#ifndef HELLO_TRIANGLE_SHADERPROGRAM_H
#define HELLO_TRIANGLE_SHADERPROGRAM_H
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <fstream>
#include <string>
#include <variant>
struct ShaderProgram {
void loadFromFile(const char* vertexFile, const char* fragmentFile);
void loadFromSource(const char* vertexSource, const char* fragmentSource);
unsigned int getProgram() const;
bool isSuccess() const;
const std::string_view& getErrorMessage() const;
private:
std::variant<unsigned int, std::string_view> data;
};
#endif //HELLO_TRIANGLE_SHADERPROGRAM_H
ShaderProgram.cpp
#include "ShaderProgram.h"
void ShaderProgram::loadFromSource(const char *vertexSource, const char *fragmentSource)
{
unsigned int vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderId, 1, &vertexSource, nullptr);
glCompileShader(vertexShaderId);
int success;
char log[512];
glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShaderId, 512, nullptr, log);
data = log;
return;
}
unsigned int fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderId, 1, &fragmentSource, nullptr);
glCompileShader(fragmentShaderId);
glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShaderId, 512, nullptr, log);
data = log;
return;
}
unsigned int program = glCreateProgram();
glAttachShader(program, vertexShaderId);
glAttachShader(program, fragmentShaderId);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(program, 512, nullptr, log);
data = log;
return;
}
data = program;
glDeleteShader(vertexShaderId);
glDeleteShader(fragmentShaderId);
}
void ShaderProgram::loadFromFile(const char *vertexFilePath, const char *fragmentFilePath)
{
try
{
std::ifstream vertexFile(vertexFilePath);
std::string vertexFileContent((std::istreambuf_iterator<char>(vertexFile)), std::istreambuf_iterator<char>());
std::ifstream fragmentFile(fragmentFilePath);
std::string fragmentFileContent((std::istreambuf_iterator<char>(fragmentFile)),
std::istreambuf_iterator<char>());
loadFromSource(vertexFileContent.c_str(), fragmentFileContent.c_str());
} catch (std::exception &e)
{
data = e.what();
}
}
unsigned int ShaderProgram::getProgram() const
{
return std::get<unsigned int>(data);
}
bool ShaderProgram::isSuccess() const
{
return std::holds_alternative<unsigned int>(data);
}
const std::string_view &ShaderProgram::getErrorMessage() const
{
return std::get<std::string_view>(data);
}
Please review this and advise is there a better way to this. Thanks in advance.
UPDATE: Implementing suggestions mentioned in the answer, please review this aswell as development is a constant process. Thanks all in advance.
Shader.h
namespace shader_program
{
enum class ShaderType
{
VERTEX,
FRAGMENT
};
enum class SourceType
{
RAW,
FILE
};
struct Shader
{
Shader(const std::string_view &source, ShaderType shaderType, SourceType sourceType);
~Shader();
unsigned int getId() const;
private:
unsigned int shader = 0;
};
struct ShaderProgram
{
ShaderProgram(const std::string_view &vertexSource, const std::string_view &fragmentSource,
SourceType vertexSourceType, SourceType fragmentSourceType);
~ShaderProgram();
void use() const;
private:
unsigned int program;
};
} // shader_program
Shader.cpp
shader_program::Shader::Shader(const std::string_view &source, shader_program::ShaderType shaderType,
shader_program::SourceType sourceType)
{
auto content = source.data(); // Assuming raw data
if (sourceType == SourceType::FILE)
{
std::ifstream ifs(source.data());
if (!ifs.good())
{
throw std::runtime_error("Invalid shader file");
}
content = std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()).data();
if (!ifs.eof())
{
throw std::runtime_error("Invalid shader file");
}
}
shader = glCreateShader(shaderType == ShaderType::VERTEX ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
glShaderSource(shader, 1, &content, nullptr);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
int len;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
if (len == 0)
{
glDeleteShader(shader);
shader = 0;
throw std::runtime_error("Could not compile shader and could not obtain any log");
}
std::vector<char> log(len);
glGetShaderInfoLog(shader, log.size(), &len, log.data());
glDeleteShader(shader);
shader = 0;
throw std::runtime_error({log.begin(), log.end()});
}
}
shader_program::Shader::~Shader()
{
// No need to delete not created shader
if (shader != 0)
{
glDeleteShader(shader);
}
}
unsigned int shader_program::Shader::getId() const
{
return shader;
}
shader_program::ShaderProgram::ShaderProgram(const std::string_view &vertexSource,
const std::string_view &fragmentSource,
shader_program::SourceType vertexSourceType,
shader_program::SourceType fragmentSourceType)
{
Shader vertexShader(vertexSource, ShaderType::VERTEX, vertexSourceType);
Shader fragmentShader(fragmentSource, ShaderType::FRAGMENT, fragmentSourceType);
program = glCreateProgram();
if (program == 0)
{
throw std::runtime_error("Could not create a shader program");
}
glAttachShader(program, vertexShader.getId());
int error = glGetError();
if (error != GL_NO_ERROR)
{
std::string errorStr = "Attaching vertex shader failed with code: " + std::to_string(error);
glDeleteProgram(program);
program = 0;
throw std::runtime_error(errorStr);
}
glAttachShader(program, fragmentShader.getId());
error = glGetError();
if (error != GL_NO_ERROR)
{
std::string errorStr = "Attaching fragment shader failed with code: " + std::to_string(error);
glDeleteProgram(program);
program = 0;
throw std::runtime_error(errorStr);
}
glLinkProgram(program);
int success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success)
{
int len;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
if (len == 0)
{
glDeleteProgram(program);
program = 0;
throw std::runtime_error("Could not link program and could not obtain any log");
}
std::vector<char> log(len);
glGetProgramInfoLog(program, log.size(), &len, log.data());
glDeleteProgram(program);
program = 0;
throw std::runtime_error({log.begin(), log.end()});
}
}
shader_program::ShaderProgram::~ShaderProgram()
{
// No need to delete not created program
if (program != 0)
{
glDeleteProgram(program);
}
}
void shader_program::ShaderProgram::use() const
{
if (program == 0)
{
throw std::runtime_error("Cannot use not created program");
}
glUseProgram(program);
}
Thanks again.