C/C++ Quickstart
Get up and running with EiplGrader for C and C++ in minutes.
Prerequisites
- GCC/G++ compiler
- Python 3.7+ (for running EiplGrader)
- OpenAI API key (or compatible LLM API)
Installation
pip install eiplgrader
C Example
1. Generate C Code
from eiplgrader.codegen import CodeGenerator
# Initialize the code generator for C
api_key = "your-openai-api-key"
generator = CodeGenerator(api_key, client_type="openai", language="c")
# Generate code from a student's explanation
result = generator.generate_code(
student_response="that counts the number of vowels in a string",
function_name="countVowels",
gen_type="cgbg"
)
print("Generated C code:")
print(result["code"][0])
Output:
int countVowels(char* str) {
int count = 0;
for (int i = 0; str[i] != '\0'; i++) {
char c = tolower(str[i]);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
count++;
}
}
return count;
}
2. Test the C Code
from eiplgrader.tester import CodeTester
# Define test cases - C REQUIRES explicit type annotations
test_cases = [
{
"parameters": {"str": "Hello World"},
"parameter_types": {"str": "char*"}, # C string type
"expected": 3,
"expected_type": "int"
},
{
"parameters": {"str": "AEIOU"},
"parameter_types": {"str": "char*"},
"expected": 5,
"expected_type": "int"
},
{
"parameters": {"str": "xyz"},
"parameter_types": {"str": "char*"},
"expected": 0,
"expected_type": "int"
}
]
tester = CodeTester(
code=result["code"][0],
test_cases=test_cases,
function_name="countVowels",
language="c"
)
results = tester.run_tests()
print(f"Tests passed: {results.successes}/{results.testsRun}")
C++ Example
1. Generate C++ Code
# Initialize for C++
generator = CodeGenerator(api_key, client_type="openai", language="cpp")
# Generate code
result = generator.generate_code(
student_response="that removes duplicates from a vector of integers",
function_name="removeDuplicates",
gen_type="cgbg"
)
print("Generated C++ code:")
print(result["code"][0])
Output:
std::vector<int> removeDuplicates(std::vector<int> nums) {
std::set<int> seen;
std::vector<int> result;
for (int num : nums) {
if (seen.insert(num).second) {
result.push_back(num);
}
}
return result;
}
2. Test the C++ Code
# C++ test cases with STL containers
test_cases = [
{
"parameters": {"nums": [1, 2, 2, 3, 3, 3, 4]},
"parameter_types": {"nums": "std::vector<int>"},
"expected": [1, 2, 3, 4],
"expected_type": "std::vector<int>"
},
{
"parameters": {"nums": [5, 5, 5, 5]},
"parameter_types": {"nums": "std::vector<int>"},
"expected": [5],
"expected_type": "std::vector<int>"
}
]
tester = CodeTester(
code=result["code"][0],
test_cases=test_cases,
function_name="removeDuplicates",
language="cpp"
)
Language-Specific Features
C Type System
Generic Type | C Type | Example |
---|---|---|
int | int | 42 |
double | double | 3.14 |
string | char* | "hello" |
bool | int | 1 or 0 |
List[int] | int* | Array pointer |
C++ Type System
Generic Type | C++ Type | Example |
---|---|---|
int | int | 42 |
double | double | 3.14 |
string | std::string | "hello" |
bool | bool | true |
List[int] | std::vector<int> | {1, 2, 3} |
Required Headers
The test harness automatically includes necessary headers:
C:
<stdio.h>
<stdlib.h>
<string.h>
<stdbool.h>
<ctype.h>
C++:
<iostream>
<vector>
<string>
<algorithm>
<set>
<map>
Common Patterns
C: Array Operations with Size
# C arrays need explicit size parameters
result = generator.generate_code(
student_response="that finds the sum of an integer array",
function_name="arraySum"
)
test_cases = [
{
"parameters": {"arr": [1, 2, 3, 4, 5], "size": 5},
"parameter_types": {"arr": "int*", "size": "int"},
"expected": 15,
"expected_type": "int"
}
]
C: String Manipulation
result = generator.generate_code(
student_response="that reverses a string in place",
function_name="reverseString"
)
test_cases = [
{
"parameters": {"str": "hello"},
"parameter_types": {"str": "char*"},
"expected": "olleh",
"expected_type": "char*",
"inplace": "1" # Modifies string in place
}
]
C++: STL Containers
result = generator.generate_code(
student_response="that merges two sorted vectors",
function_name="mergeSorted"
)
test_cases = [
{
"parameters": {
"vec1": [1, 3, 5],
"vec2": [2, 4, 6]
},
"parameter_types": {
"vec1": "std::vector<int>",
"vec2": "std::vector<int>"
},
"expected": [1, 2, 3, 4, 5, 6],
"expected_type": "std::vector<int>"
}
]
C++: String Operations
result = generator.generate_code(
student_response="that splits a string by spaces",
function_name="splitString"
)
test_cases = [
{
"parameters": {"str": "hello world test"},
"parameter_types": {"str": "std::string"},
"expected": ["hello", "world", "test"],
"expected_type": "std::vector<std::string>"
}
]
Memory Management Considerations
C: Manual Memory
# Be careful with dynamically allocated memory
# The test harness handles basic cases but avoid complex allocations
result = generator.generate_code(
student_response="that creates an array of fibonacci numbers",
function_name="fibonacci"
)
# Prefer stack-allocated arrays or pass pre-allocated memory
test_cases = [
{
"parameters": {"n": 5, "result": [0, 0, 0, 0, 0]},
"parameter_types": {"n": "int", "result": "int*"},
"expected": [0, 1, 1, 2, 3],
"expected_type": "int*",
"inplace": "1" # Fills the provided array
}
]
C++: RAII and Smart Pointers
C++ code typically uses RAII (vectors, strings) which handles memory automatically:
# C++ STL containers manage their own memory
test_cases = [
{
"parameters": {"size": 1000000},
"parameter_types": {"size": "int"},
"expected": 1000000, # Large vector created and destroyed safely
"expected_type": "int"
}
]
In-Place Modifications
C: Pointer-based Modifications
# Mode 1: Modifies through pointer
test_case = {
"parameters": {"arr": [3, 1, 4, 1, 5], "size": 5},
"parameter_types": {"arr": "int*", "size": "int"},
"expected": [1, 1, 3, 4, 5],
"expected_type": "int*",
"inplace": "1" # Sorts array in place
}
C++: Reference-based Modifications
# C++ can use references for in-place modifications
test_case = {
"parameters": {"vec": [3, 1, 4, 1, 5]},
"parameter_types": {"vec": "std::vector<int>"},
"expected": [1, 1, 3, 4, 5],
"expected_type": "std::vector<int>",
"inplace": "1" # Modifies vector in place
}
Compilation Details
C Compilation
- Compiler:
gcc
- Standard: C99 by default
- Flags:
-o output_file input_file.c
C++ Compilation
- Compiler:
g++
- Standard: C++17 (
-std=c++17
) - Flags:
-std=c++17 -o output_file input_file.cpp
Error Handling
Both C and C++ provide compilation and runtime error information:
try:
results = tester.run_tests()
if not results.was_successful():
for result in results.test_results:
if not result["pass"]:
print(f"Test failed: {result['function_call']}")
if "Compilation failed" in str(result["error"]):
print("Compilation error - check syntax and types")
elif "Segmentation fault" in str(result["error"]):
print("Memory access error - check array bounds")
except Exception as e:
print(f"Error: {e}")
Best Practices
For C:
- Always specify array sizes:
# C arrays need explicit size "parameters": {"arr": [1, 2, 3], "n": 3} "parameter_types": {"arr": "int*", "n": "int"}
- Use appropriate string type:
"parameter_types": {"str": "char*"} # not "string"
- Handle null termination:
// Generated code should handle '\0' for strings
For C++:
- Prefer STL containers:
"parameter_types": {"nums": "std::vector<int>"} # not int*
- Use std:: prefix:
"std::string" # not just "string" "std::vector<int>" # not "vector<int>"
- Leverage C++ features:
// Range-based for loops // auto keyword // STL algorithms
Common Gotchas
- C String Literals: Test harness handles string allocation
# This works - test harness allocates memory "parameters": {"str": "hello"}
- Array Return Types: C can’t return arrays directly
// Use out parameters or dynamic allocation void processArray(int* input, int* output, int size);
- C++ Template Errors: Can be verbose
# Be specific with types to avoid template issues "std::vector<int>" not "std::vector"
Next Steps
- Explore Go Quickstart for a modern compiled language
- Learn about Test Case Format for complex types
- See Language Support for detailed C/C++ info
- Try Haskell Quickstart for functional programming